Imports WRDB.Project
Imports WRDB.DBProviders
Imports System.Data.Common

Public Class frmGIS

#Region "Declarations"

    'most recently used file list...
    Dim mru As clsMruStripMenu

    Private Structure structAnimate
        Dim dsData As WRDB.DataSource.clsData 'for animation results
        Dim DataLayer As DotSpatial.Controls.MapPolygonLayer
        Dim DataLayerName, DataField As String 'mapping to polygon shapefile
        Dim StationList As Generic.List(Of String) 'list of data stations IDs found in data file
        Dim CurrentResults As Generic.Dictionary(Of String, Single) 'dictionary of values at each station for current time step
        Dim DataHandle As Integer
        Dim MinDate, MaxDate, ThisDate, NextDate As Date
        Dim MinValue, MaxValue As Double
        Dim MinNum, MaxNum As Integer
        Dim TimeStep As Double 'time step in days
        Dim TimeUnits As enumTimeUnits
        Dim Speed As Integer 'relative speed from 1 to 5 (1 is slowest)
        Dim Colors() As Color '5 colors for below threshold value, and 0, 1/3, 2/3, and 1 intensities
        Dim InvertColor As Boolean
        Dim ThresholdType As enumThreshold
        Dim ThresholdValue As Double
        Dim IsAnimating As Boolean
    End Structure

    Friend Enum enumThreshold
        None
        ColorAbove
        ColorBelow
    End Enum

    Private Enum enumDisplay
        None
        NumResult
        MinResult
        MaxResult
        AvgResult
    End Enum

    Private Enum enumAggregate
        POR
        None
        Month
        Growing
        Annual
    End Enum

    Private Enum enumField
        ID
        Name
        Type
        Lat
        Lon
        Freq
        Elev
        Branch
        Reach
        RMile
        Area
        Agency
        RefID
        Remarks
        Other
        HUC
    End Enum

    ''' <summary>
    ''' Controls how data are displayed in GIS form
    ''' </summary>
    Public Enum enumMode
        ''' <summary>Display all stations from support table</summary>
        DisplayStations

        ''' <summary>Select from all stations found in browser</summary>
        SelectStations

        ''' <summary>Display summary from last query</summary>
        OutputData

        ''' <summary>Allow data files to be loaded and associated with polygon shapefile; disables display of WRDB Stations data</summary>
        Animate

        ''' <summary>Allow selection of lat/long rectangle</summary>
        AreaOfInterest

        ''' <summary>Display all stations supplied in tab-separated list like Station ID/Name(tab)Latitude(tab)Longitude</summary>
        StationsList
    End Enum

    Private Structure structMode
        Dim LastSettingsFilename As String
        Dim DataItemsVisible As Boolean
        Dim AllowAnimation As Boolean
        Dim AllowStationSelection As Boolean
        Dim DisplayCoordsByType As Boolean
        Dim SelectStationsFromDataTable As Boolean
        Dim DataTableName As String
        Sub Initialize(ByVal WRDBShapeDirectory As String, ByVal Mode As enumMode)
            LastSettingsFilename = WRDBShapeDirectory & IIf(Mode = enumMode.Animate, "\LastAnim.GIS", "\LastGIS.GIS")
            DataItemsVisible = (Mode = enumMode.Animate)
            AllowAnimation = (Mode = enumMode.Animate Or Mode = enumMode.OutputData)
            AllowStationSelection = (Mode = enumMode.SelectStations Or Mode = enumMode.StationsList)
            DisplayCoordsByType = (Mode = enumMode.DisplayStations Or Mode = enumMode.SelectStations)
            If Mode = enumMode.SelectStations Then DataTableName = WRDBProject.FullTableName(ActiveTableName) Else DataTableName = TempData
            SelectStationsFromDataTable = (Mode = enumMode.OutputData OrElse (Mode = enumMode.SelectStations AndAlso Not WRDBProject.GisQuickTable))
        End Sub
    End Structure

    Private Enum enumTimeUnits
        Minute
        Hour
        Day
        Month
        Year
    End Enum

    ''' <summary>
    ''' Temporary assignments for growing season limits (later allow to be user-defined)
    ''' </summary>
    Const GrowingMonth1 = 3, GrowingDay1 = 15, GrowingMonth2 = 11, GrowingDay2 = 15

    Private ActiveLayer As clsLayer

    Private Aggregate As enumAggregate

    Private AnimationInfo As structAnimate

    '''' <summary>Data table containing summary information for each station id and each aggregate timestep in data file attached to shape file used for animation</summary>
    'Private dtPolygon As DataTable

    ''' <summary>
    ''' Data table containing summary information for each WRDB station
    ''' </summary>
    Private dtResults As DataTable

    ''' <summary>Name of last .GIS file loaded or saved as</summary>
    Private GISFileName As String = ""

    ' ''' <summary>Subclassed MapWinGIS control that allows me to handle mouse wheel zooms</summary>
    Friend WithEvents GISMap As New DotSpatial.Controls.Map ' MouseWheelMap

    ''' <summary>Flag set whenever anything stored in .GIS file changed by user</summary>
    Private IsModified As Boolean = False

    ''' <summary>Flag indicating whether stations are being selected using crossing box</summary>
    Private IsSelecting As Boolean = False

    ''' <summary>Cached pixel coordinates of each WRDB stations; to speed up tooltip display when hovering over stations</summary>
    Private lstCoords As New Generic.List(Of Point)

    ''' <summary>List of selected stations</summary>
    Private lstStations As New Generic.List(Of String)

    ''' <summary>The form mode, controlling how data are displayed</summary>
    Friend Mode As enumMode

    ''' <summary>Numerous settings that control form based on mode value</summary>
    Private ModeSettings As structMode

    ' ''' <summary>Handle of WRDBShapefile</summary>
    'Private WRDBLayerHandle As Integer

    ''' <summary>Layer of WRDBShapefile</summary>
    Private WRDBLayer As DotSpatial.Controls.MapPointLayer

    ''' <summary>Temporary directory where WRDB Stations shapefile is saved</summary>
    Private WRDBShapeDirectory, WRDBShapeFileName As String

    ''' <summary>Name of WRDB Stations shapefile created dynamically</summary>
    Private WRDBShapefile As DotSpatial.Data.FeatureSet

    ''' <summary>If true, animation parameters will not be updated (used when multiple comboboxes need to be updated during LoadSettings)</summary>
    Private SuspendMapUpdate As Boolean

    ''' <summary>Handle of drawing layer for AOI rectangle</summary>
    Private AOIDrawingHandle As Integer = -1

    ''' <summary>Coordinates of AOI rectangle</summary>
    Private _AOIRectangle As RectangleF

    ''' <summary>Set this true if want last graph to be loaded once form is loaded</summary>
    Public LoadLast As Boolean = False

    ''' <summary>Set this to full path of file if want it to be loaded once form is loaded</summary>
    Public LoadFile As String = ""

    ''' <summary>Set to true when form is closed so can possibly interrupt long-running load process</summary>
    Private IsCancelled As Boolean = False

#End Region

#Region "Drag-drop support for layer list"


    Private IsDragging As Boolean = False
    Private itemIndexDrag, itemIndexDrop As Integer

    'following needs work--probably not worth it!

    'Private Sub lstLayers_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles lstLayers.DragEnter
    '    If e.Data.GetDataPresent(DataFormats.StringFormat) And e.AllowedEffect = DragDropEffects.Move Then e.Effect = DragDropEffects.Move
    'End Sub

    'Private Sub lstLayers_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles lstLayers.DragOver
    '    itemIndexDrop = lstLayers.IndexFromPoint(lstLayers.PointToClient(New Point(e.X, e.Y)))
    '    IsDragging = True
    '    lstLayers.SelectedIndex = itemIndexDrop
    'End Sub

    'Private Sub lstLayers_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles lstLayers.DragDrop
    '    If e.Data.GetDataPresent(DataFormats.StringFormat) Then
    '        If itemIndexDrop >= 0 And itemIndexDrop < lstLayers.Items.Count Then
    '            lstLayers.Items.Insert(itemIndexDrop, e.Data.GetData(DataFormats.Text))
    '        Else 'add to bottom of list
    '            lstLayers.Items.Add(e.Data.GetData(DataFormats.Text))
    '        End If
    '        IsDragging = False
    '    End If
    'End Sub

    'Private Sub lstLayers_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles lstLayers.MouseDown
    '    itemIndexDrag = lstLayers.IndexFromPoint(e.X, e.Y)
    '    If itemIndexDrag >= 0 And itemIndexDrag < lstLayers.Items.Count Then
    '        lstLayers.DoDragDrop(lstLayers.Items(itemIndexDrag), DragDropEffects.Move)
    '    End If
    'End Sub

    Private Sub lstLayers_ItemCheck(ByVal sender As Object, ByVal e As System.Windows.Forms.ItemCheckEventArgs) Handles lstLayers.ItemCheck
        If ActiveLayer IsNot Nothing Then ActiveLayer.Visible = (e.NewValue = CheckState.Checked)
    End Sub

    Private Sub lstLayers_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles lstLayers.SelectedIndexChanged
        Try
            With lstLayers
                If Not IsDragging Then
                    If .SelectedIndex = -1 Then
                        ActiveLayer = Nothing
                    Else
                        ActiveLayer = clsLayer.Reset(GISMap, .Items.Count - 1 - .SelectedIndex)
                    End If
                End If
            End With
            If ActiveLayer IsNot Nothing Then
                pgLayers.SelectedObject = ActiveLayer
                CurrentLayer = ActiveLayer.Layer  'for typeconverter
            End If

            'clear info on prior object hovered over
            txtInfo.Text = ""

            'clear selected shapes and only allow shapes in active layer to be selected
            With GISMap
                .Layers.SuspendEvents()
                Dim ext As New DotSpatial.Topology.Envelope(.ViewExtents.X, .ViewExtents.Y, .ViewExtents.X + .ViewExtents.Width, .ViewExtents.Y + .ViewExtents.Height)
                .ClearSelection(ext)
                For Each lyr As DotSpatial.Controls.IMapLayer In .Layers
                    lyr.SelectionEnabled = (ActiveLayer IsNot Nothing AndAlso lyr.LegendText = ActiveLayer.Name)
                Next
                .Layers.ResumeEvents()
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub
#End Region

#Region "Form events"


    ''' <summary>
    ''' Instantiate new GIS form and initialize (will always load last GIS file associated with mode)
    ''' </summary>
    ''' <param name="_WRDBProject">WRDB Project (to get default settings, etc.)</param>
    ''' <param name="_TableName">Currently active data table (Select mode)</param>
    ''' <param name="_TempData">Name of temp table (Display mode)</param>
    ''' <param name="_Mode">GIS display mode</param>
    ''' <param name="_WRDBStationTypes">List of available station types to speed processing</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal _WRDBProject As clsProject, ByVal _TableName As String, ByVal _TempData As String, ByVal _Mode As frmGIS.enumMode, ByVal _WRDBStationTypes As Generic.List(Of String), _BaseMap As String)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        WRDBProject = _WRDBProject
        ActiveTableName = _TableName
        TempData = _TempData
        WRDBStationTypes = _WRDBStationTypes
        GISForm = Me
        LoadFile = _BaseMap
        Mode = _Mode
        'ModeSettings.Initialize(Mode)
    End Sub

    ''' <summary>
    ''' Instantiate new GIS form and initialize
    ''' </summary>
    ''' <param name="GisFile">GIS file to load</param>
    ''' <param name="LoadLast">Flag indicating if last map should be loaded</param>
    ''' <remarks></remarks>
    Public Sub New(Optional ByVal GisFile As String = "", Optional ByVal LoadLast As Boolean = False)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.LoadFile = GisFile
        Me.LoadLast = LoadLast
        WRDBProject = Nothing
        ActiveTableName = ""
        TempData = ""
        GISForm = Me
        WRDBStationTypes.Clear()
        Mode = enumMode.Animate
        'ModeSettings.Initialize(Mode)
    End Sub

    ''' <summary>
    ''' Instantiate new GIS form and initialize
    ''' </summary>
    ''' <param name="_Mode">GIS display mode</param>
    ''' <param name="_StationList">Tab-separated stations list like Station ID/Name(tab)Latitude(tab)Longitude</param>
    Public Sub New(ByVal _Mode As frmGIS.enumMode, Optional ByVal _StationList As Generic.List(Of String) = Nothing, Optional _Basemap As String = "")

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Me.LoadFile = _Basemap
        WRDBProject = Nothing
        ActiveTableName = ""
        TempData = ""
        GISForm = Me
        StationList = _StationList
        WRDBStationTypes.Clear()
        Mode = _Mode
        'ModeSettings.Initialize(Mode)
    End Sub

    Private Sub CheckDropDownItem(ByVal ParentMenuItem As Object, ByVal Index As Integer)
        For i As Integer = 0 To ParentMenuItem.DropDownItems.Count - 1
            CType(ParentMenuItem.DropDownItems(i), ToolStripMenuItem).Checked = i = Index
        Next
    End Sub

    Private Sub CheckDropDownItem(ByVal ParentMenuItem As ToolStripMenuItem, ByVal DropDownItem As ToolStripMenuItem)
        For i As Integer = 0 To ParentMenuItem.DropDownItems.Count - 1
            CType(ParentMenuItem.DropDownItems(i), ToolStripMenuItem).Checked = ParentMenuItem.DropDownItems(i) Is DropDownItem
        Next
    End Sub

    Private Sub frmGIS_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        'note: not called if parent WRDB application exits
        Try
            ' Call sub to save the MRU filenames
            mru.SaveToRegistry()

            If Not My.Computer.FileSystem.DirectoryExists(TempPath) Then My.Computer.FileSystem.CreateDirectory(TempPath)
            'SaveSettings(ModeSettings.LastSettingsFilename)
            If dtResults IsNot Nothing Then dtResults.Dispose()
            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()
            'GISForm.Dispose()
            GISForm = Nothing
            If WRDBStationTypes IsNot Nothing Then WRDBStationTypes.Clear()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub frmGIS_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        Try
            If IsModified And GISMap.Layers.Count > 0 AndAlso Not (GISMap.Layers.Count = 1 AndAlso WRDBShapefile IsNot Nothing) Then
                Select Case SaveChangesWarning(GISFileName)
                    Case Windows.Forms.DialogResult.Yes : e.Cancel = Not SaveSettings(GISFileName)
                    Case Windows.Forms.DialogResult.No
                    Case Windows.Forms.DialogResult.Cancel : e.Cancel = True
                End Select
            End If
            If e.Cancel Then IsCancelled = True : Application.DoEvents() : Exit Sub
            SaveWindowPos(REGAPP, Me)
            SaveSetting(REGAPP, Me.Name, "SplitMainCollapsed", splitMain.IsCollapsed)
            SaveSetting(REGAPP, Me.Name, "SplitMainDistance", splitMain.ExpandedDistance)
            SaveSetting(REGAPP, Me.Name, "SplitLayersDistance", splitLayers.SplitterDistance)
            SaveSetting(REGAPP, Me.Name, "SplitInfoDistance", splitInfo.SplitterDistance)
            SaveSetting(REGAPP, Me.Name, "Aggregate", Aggregate)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub frmGIS_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Try
            Application.EnableVisualStyles()
            'If MainForm IsNot Nothing Then MainForm.WriteProgress("Preparing GIS form...")
            WriteProgress("Preparing GIS form...")
            GetWindowPos(REGAPP, Me)
            splitMain.IsCollapsed = GetSetting(REGAPP, Me.Name, "SplitMainCollapsed", False)
            splitMain.ExpandedDistance = GetSetting(REGAPP, Me.Name, "SplitMainDistance", 200)
            ''must do this below--it is not working properly!!!!! they keep shrinking!
            'Try
            '    splitInfo.SplitterDistance = GetSetting(REGAPP, Me.Name, "SplitInfoDistance", 200)
            '    splitLayers.SplitterDistance = GetSetting(REGAPP, Me.Name, "SplitLayersDistance", 400)
            '    'Debug.Print(splitInfo.SplitterDistance & "," & splitLayers.SplitterDistance)
            'Catch ex As Exception
            'End Try
            Aggregate = GetSetting(REGAPP, Me.Name, "Aggregate", enumAggregate.POR)

            'initialize mru (most recently used) menu list

            mru = New clsMruStripMenu(mnuFileRecent, AddressOf RecentFileHandler, "Software\VB and VBA Program Settings\WRDBGRAPH\GIS Files", 9)
            With mru
                .MaxShortenPathLength = 30
                .LoadFromRegistry()

                'make sure all these mru files exist
                For i As Integer = .NumEntries - 1 To 0 Step -1
                    If Not My.Computer.FileSystem.FileExists(.GetFileAt(i)) Then .RemoveFile(i)
                Next
                .SaveToRegistry()
            End With

            Try
                Me.splitMain.Panel2.Controls.Add(GISMap)
            Catch ex As Exception
                ErrorMsg("Unable to display GIS form because the MapWindow control has not been installed. Please download and install it from the WRDB web site.", ex)
                Close()
                Exit Sub
            End Try

            With GISMap
                .Dock = DockStyle.Fill
                .ProjectionModeDefine = DotSpatial.Controls.ActionMode.Never
                .ProjectionModeReproject = DotSpatial.Controls.ActionMode.Never
                .ContextMenuStrip = ContextMenuMap
            End With
            GISForm = Me
            'ProjectProjection = Nothing
            ProjectProjection = New DotSpatial.Projections.ProjectionInfo()
            ProjectProjection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984

            WRDBShapeDirectory = TempPath

            'if project's GIS directory has been set and exists, use it instead of temporary directory

            If WRDBProject IsNot Nothing AndAlso WRDBProject.GisDirectory <> "" AndAlso My.Computer.FileSystem.DirectoryExists(WRDBProject.GisDirectory) Then WRDBShapeDirectory = WRDBProject.GisDirectory

            WRDBShapeFileName = String.Format("{0}\{1}.shp", WRDBShapeDirectory, WRDBFileName)

            For i As Integer = 0 To DefaultLayerColor.Length - 1
                DefaultLayerColor(i) = Choose((i Mod 14) + 1, Color.Red, Color.Blue, Color.Green, Color.LightCyan, Color.Magenta, Color.DarkOrange, Color.Gray, Color.Black,
                                                              Color.DarkRed, Color.DarkBlue, Color.DarkGreen, Color.DarkCyan, Color.DarkMagenta, Color.Brown)
            Next

            With AnimationInfo
                ReDim .Colors(4)
                .Colors(0) = Color.LightGray
                .Colors(1) = Color.Green
                .Colors(2) = Color.Yellow
                .Colors(3) = Color.Red
                .Colors(4) = Color.White
                .ThresholdType = enumThreshold.None
                .ThresholdValue = 0.0
                .InvertColor = False
                .Speed = 3
            End With

            IsModified = False

            ModeSettings.Initialize(WRDBShapeDirectory, Mode)

            'retrieve default toolbar settings, but override depending on the mode

            frmToolbar.GetToolbarSettings(tbMain)

            'control visibility of data items
            Dim tf As Boolean = ModeSettings.DataItemsVisible
            mnuFileSepData.Visible = tf
            mnuFileOpen.Visible = tf
            mnuFileOpenTable.Visible = tf
            mnuFileClose.Visible = tf
            mnuViewData.Visible = tf
            mnuViewLegend.Visible = tf
            mnuDataDateTime.Visible = tf
            mnuDataSepOptions.Visible = tf
            mnuDataOptions.Visible = tf
            tbSepData.Visible = tf
            tbOpen.Visible = tf
            tbClose.Visible = tf

            tf = ModeSettings.AllowAnimation

            'if animating, made these visible
            mnuFileExport.Visible = tf
            mnuFileSepAnim.Visible = tf
            mnuFileMovie.Visible = tf
            mnuViewSepAnimate.Visible = tf
            mnuViewAnimate.Visible = tf
            mnuViewAnimateSpeed.Visible = tf
            mnuDataDateTime.Visible = tf
            mnuDataAggregate.Visible = tf
            mnuDataPCode.Visible = tf
            trkAnim.Visible = tf
            tbAnim.Visible = tf
            cboPCode.Visible = tf
            cboAggregate.Visible = tf

            '...otherwise
            lblLocation.Visible = Not tf
            txtLocation.Visible = Not tf
            btnLocation.Visible = Not tf
            sepLocation.Visible = Not tf

            tbMain.Refresh()
            Application.DoEvents()

            GISMap.Visible = False
            SuspendMapUpdate = True

            'control contents of toolbar combos and submenus
            Select Case Mode
                Case enumMode.OutputData, enumMode.Animate
                    cboDisplay.Items.AddRange(New String() {"No. of Observations", "Minimum Values", "Maximum Values", "Average Values"})
                    cboDisplay.SelectedIndex = 0
                    With mnuDataDisplay.DropDownItems
                        .Add("No. of Observations")
                        .Add("Minimum Values")
                        .Add("Maximum Values")
                        .Add("Average Values")
                    End With
                    CheckDropDownItem(mnuDataDisplay, 0)

                    If Mode = enumMode.OutputData Then
                        For Each s As String In WRDBProject.DB.GetRecordList(IIf(WRDBProject.GisQuickList, "PCodes", TempData), "PCODE")
                            cboPCode.Items.Add(s)
                            mnuDataPCode.DropDownItems.Add(s)
                        Next
                    End If
                    If cboPCode.Items.Count > 0 Then
                        cboPCode.SelectedIndex = 0
                        CheckDropDownItem(mnuDataPCode, 0)
                    End If

                    CheckDropDownItem(mnuDataAggregate, Aggregate)
                    tbAnim.Visible = Aggregate <> enumAggregate.POR
                    cboAggregate.SelectedIndex = 0

                    With AnimationInfo
                        CheckDropDownItem(mnuViewAnimateSpeed, .Speed - 1)
                        CheckDropDownItem(tbAnimSpeed, .Speed - 1)
                    End With

                Case enumMode.DisplayStations
                    WRDBProject.ResetPDox("Stations")
                    cboDisplay.Items.Add("All Stations")
                    mnuDataDisplay.DropDownItems.Add("All Stations")
                    For Each s As String In WRDBProject.DB.GetRecordList("Stations", "Station_Type")
                        cboDisplay.Items.Add(s)
                        mnuDataDisplay.DropDownItems.Add(s)
                    Next
                    cboDisplay.SelectedIndex = 0
                    CheckDropDownItem(mnuDataDisplay, 0)

                Case enumMode.SelectStations, enumMode.StationsList
                    cboDisplay.Items.Add("All Stations")
                    mnuDataDisplay.DropDownItems.Add("All Stations")
                    If WRDBStationTypes.Count = 0 Then
                        Dim SQL As String
                        If Mode = enumMode.StationsList Then
                            For Each s As String In StationList
                                Dim StationType As String = s.Split(vbTab)(2)
                                If StationType <> "" And Not cboDisplay.Items.Contains(StationType) Then
                                    cboDisplay.Items.Add(StationType)
                                    mnuDataDisplay.DropDownItems.Add(StationType)
                                End If
                            Next
                        Else
                            WRDBProject.ResetPDox("Stations")
                            If WRDBProject.GisQuickList Then
                                SQL = String.Format("SELECT DISTINCT Station_Type FROM STATIONS", WRDBProject.FullTableName(ActiveTableName))
                            Else
                                SQL = String.Format("SELECT DISTINCT Station_Type FROM STATIONS,{0} WHERE STATIONS.STATION_ID={0}.STATION_ID", WRDBProject.FullTableName(ActiveTableName))
                            End If
                            Dim dr As DbDataReader = WRDBProject.DB.ExecuteReader(SQL)
                            While dr IsNot Nothing AndAlso dr.Read
                                If Not dr.IsDBNull(0) Then
                                    cboDisplay.Items.Add(dr.GetString(0))
                                    mnuDataDisplay.DropDownItems.Add(dr.GetString(0))
                                End If
                            End While
                            dr.Close()
                        End If
                        With WRDBStationTypes
                            .Clear()
                            For Each s As String In cboDisplay.Items
                                .Add(s)
                            Next
                        End With
                    Else
                        For Each s As String In WRDBStationTypes
                            cboDisplay.Items.Add(s)
                            mnuDataDisplay.DropDownItems.Add(s)
                        Next
                    End If
                    cboDisplay.SelectedIndex = 0
                    CheckDropDownItem(mnuDataDisplay, 0)

                Case enumMode.AreaOfInterest
                    mnuDataDisplay.Visible = False

            End Select

            cboDisplay.MaxDropDownItems = Math.Min(25, Math.Max(1, cboDisplay.Items.Count))
            cboPCode.MaxDropDownItems = 15

            If LoadLast Then
                Dim LastGIS As String = ""
                If LastGIS <> "" Then
                    LoadSettings(LastGIS, False)
                    ResetAnimation()
                    RefreshStationsDisplay()
                End If
                'mnuFileOpenLast.PerformClick()
            End If

            Select Case Mode
                Case enumMode.DisplayStations, enumMode.SelectStations
                    CreateStationsLayer()
                Case enumMode.OutputData
                    SummarizeStationData()
                    CreateStationsLayer()
                Case enumMode.Animate
                    tbDataTable.Visible = True
                    tbLegend.Visible = True
                Case enumMode.StationsList
                    CreateStationsLayer(StationList)
                Case enumMode.AreaOfInterest
            End Select

            If lstLayers.Items.Count > 0 Then lstLayers.SelectedIndex = 0
            Me.pgLayers.SelectedObject = ActiveLayer

            'If MainForm IsNot Nothing Then MainForm.WriteProgress("Summarizing data...")
            WriteProgress("Summarizing data...")

            If Not LoadLast Then
                Select Case Mode
                    Case enumMode.Animate : LoadFile = GetSetting(REGAPP, "AutoLoad", "Animation")
                End Select

                If LoadFile <> "" Then
                    If My.Computer.FileSystem.FileExists(LoadFile) Then
                        LoadSettings(LoadFile, True)
                    Else
                        WarningMsg("The specified basemap file ({0}) could not be found.", LoadFile)
                    End If
                Else
                    cboAggregate.SelectedIndex = 0
                    GISMap.ZoomToMaxExtent()
                End If
                ResetAnimation()
            End If

            SuspendMapUpdate = False

            IsModified = False

        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
            ProgressComplete()
            GISMap.Visible = True
            RefreshStationsDisplay()
        End Try
    End Sub

    Private Sub RecentFileHandler(ByVal Number As Integer, ByVal FileName As String)
        LoadSettings(FileName, False)
        ResetAnimation()
        RefreshStationsDisplay()

        ' Call sub to reorder the MRU list
        mru.AddFile(FileName)
        mru.SaveToRegistry()
        GISFileName = FileName
        ResetTitleBar()
    End Sub

#End Region

#Region "Map Color Functions..."

    ''' <summary>
    ''' Return gradient color using 4-color transition
    ''' </summary>
    ''' <param name="Intensity">Number from 0.0 to 1.0 indicating desired relative value</param>
    ''' <param name="_Colors">Array of 5 colors associated with below threshold and smallest intensity to largest</param>
    ''' <param name="InvertColors">If true, swap order of colors</param>
    ''' <param name="ThresholdType">How thresholds are handled</param>
    ''' <param name="ThresholdValue">If type is not none, is threshold intensity</param>
    Friend Shared Function GradientColor(ByVal Intensity As Single, ByVal _Colors() As Color, ByVal InvertColors As Boolean, ByVal ThresholdType As enumThreshold, ByVal ThresholdValue As Double) As Color
        Dim Colors() As Color = _Colors.Clone
        If InvertColors Then Array.Reverse(Colors, 1, 4) 'reverse the top 4 colors (not the non-threshold value)
        If Intensity < 0.33 Then
            GradientColor = ColorCombine(Colors(1), Colors(2), (Intensity - 0.0) / 0.33)
        ElseIf Intensity < 0.67 Then
            GradientColor = ColorCombine(Colors(2), Colors(3), (Intensity - 0.33) / 0.33)
        Else
            GradientColor = ColorCombine(Colors(3), Colors(4), (Intensity - 0.67) / 0.33)
        End If
        Select Case ThresholdType
            Case enumThreshold.None
            Case enumThreshold.ColorAbove
                If Intensity < ThresholdValue Then GradientColor = Colors(0)
            Case enumThreshold.ColorBelow
                If Intensity > ThresholdValue Then GradientColor = Colors(0)
        End Select
    End Function

    ''' <summary>
    ''' Return color associated with intensity using Animation defaults
    ''' </summary>
    ''' <param name="Intensity">Number from 0.0 to 1.0 indicating desired relative value</param>
    Friend Function GradientColor(ByVal Intensity As Single) As Color
        With AnimationInfo
            Return GradientColor(Intensity, .Colors, .InvertColor, .ThresholdType, (.ThresholdValue - .MinValue) / (.MaxValue - .MinValue))
        End With
    End Function

    Private Shared Function ColorCombine(ByVal Color1 As System.Drawing.Color, ByVal Color2 As System.Drawing.Color, ByVal Opacity As Double) As System.Drawing.Color
        Opacity = Math.Min(1, Math.Max(0, Opacity))
        Return Drawing.Color.FromArgb(GammaCombine(Color1.R, Color2.R, Opacity), _
                                        GammaCombine(Color1.G, Color2.G, Opacity), _
                                        GammaCombine(Color1.B, Color2.B, Opacity))
    End Function

    Private Shared Function GammaCombine(ByVal v1 As Integer, ByVal v2 As Integer, ByVal a As Double)
        Dim d1 As Double = v1 / 255.0
        Dim d2 As Double = v2 / 255.0
        Dim f As Double = 2.2
        Dim r As Integer = Int(Math.Pow(Math.Pow(d1, f) * (1 - a) + Math.Pow(d2, f) * a, 1 / f) * 255 + 0.5)
        Return Math.Min(255, r)
    End Function

    Private Function ColorToUInt(ByVal c As Color) As UInt32
        Return Convert.ToUInt32(System.Drawing.ColorTranslator.ToWin32(c))
    End Function

#End Region

    Private Sub CustomizeToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuCustomize.Click
        With New frmToolbar(tbMain)
            .ShowDialog(Me)
            .Dispose()
        End With
    End Sub

    ''' <summary>
    ''' Used to find the data row associated with the station that was just clicked on
    ''' </summary>
    ''' <param name="ScreenX">X Mouse click position in pixels</param>
    ''' <param name="ScreenY">Y Mouse click position in pixels</param>
    ''' <returns>Datarow containing station clicked on (or Nothing if didn't click close enough)</returns>
    Private Function FindShapes(ByVal ScreenX As Integer, ByVal ScreenY As Integer) As Generic.List(Of Integer)
        Dim lst As New Generic.List(Of Integer)
        Try
            Const WindowSize As Integer = 5

            With WRDBLayer

                'following cache speeds up search--only computed when extents change
                If lstCoords.Count = 0 Then
                    IsCancelled = False
                    For r As Integer = 0 To .DataSet.Features.Count - 1
                        If IsCancelled Or .DataSet.Features(r) Is Nothing Then Return lst
                        If GISMap.ViewExtents.Intersects(.DataSet.Features(r).Envelope) Then 'only compute if in view extents, otherwise add bogus point
                            lstCoords.Add(GISMap.ProjToPixel(.DataSet.Features(r).Coordinates(0)))
                        Else
                            lstCoords.Add(New Point(0, 0))
                        End If
                        If r Mod 1000 = 0 Then Application.DoEvents()
                    Next
                End If

                For r As Integer = 0 To Math.Min(lstCoords.Count, .DataSet.Features.Count) - 1
                    Dim p As Point = lstCoords(r)
                    Dim typeMatches As Boolean = True
                    If ModeSettings.DisplayCoordsByType Then
                        Dim value As String = TestNull(.DataSet.DataTable.Rows(r)(enumField.Type), "")
                        typeMatches = (cboDisplay.Text = value OrElse (cboDisplay.SelectedIndex = 0 AndAlso cboDisplay.Items.Contains(value)))
                    End If
                    If typeMatches AndAlso Math.Abs(ScreenX - p.X) < WindowSize AndAlso Math.Abs(ScreenY - p.Y) < WindowSize Then lst.Add(r)
                Next
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
        Return lst
    End Function

    Private Function Invert(ByVal clr As Color) As Color
        Return Color.FromArgb(Not clr.R, Not clr.G, clr.B)
    End Function

    Private Function SaveSettings(ByRef _FileName As String, Optional ByVal SaveAs As Boolean = False) As Boolean
        Try
            If SaveAs Or _FileName = "" Then
                With New SaveFileDialog
                    .AddExtension = True
                    .CheckPathExists = True
                    .CreatePrompt = False
                    .DefaultExt = ".GIS"
                    .Filter = "WRDB GIS File (*.GIS)|*.GIS"
                    .FilterIndex = 0
                    .OverwritePrompt = True
                    .Title = "Save GIS File"
                    .ValidateNames = True
                    If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                        _FileName = .FileName
                        ResetTitleBar()
                        .Dispose()
                    Else
                        .Dispose()
                        Return False
                    End If
                End With
            End If

            'save general settings and last layers loaded

            Dim IniFile As New clsWRDBIniFile(_FileName)
            With IniFile
                .Create()
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.NumGIS, CStr(lstLayers.Items.Count))
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMin, GISMap.ViewExtents.MinX.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMax, GISMap.ViewExtents.MaxX.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMin, GISMap.ViewExtents.MinY.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMax, GISMap.ViewExtents.MaxY.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Display, cboDisplay.Text)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_PCode, cboPCode.Text)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Aggregate, Aggregate.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DateTime, AnimationInfo.ThisDate.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_AnimSpeed, AnimationInfo.Speed.ToString)

                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataLayer, AnimationInfo.DataLayerName)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataField, AnimationInfo.DataField)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor0, AnimationInfo.Colors(0).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor1, AnimationInfo.Colors(1).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor2, AnimationInfo.Colors(2).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor3, AnimationInfo.Colors(3).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor4, AnimationInfo.Colors(4).Name)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataInvert, AnimationInfo.InvertColor.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdType, AnimationInfo.ThresholdType.ToString)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdValue, AnimationInfo.ThresholdValue.ToString)

                Dim FileName As String = ""
                If AnimationInfo.dsData IsNot Nothing Then FileName = AnimationInfo.dsData.FileName
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFile, FileName)
                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFileRelative, GetRelativePath(IO.Path.GetDirectoryName(.FileName), FileName))

                GISMap.Layers.SuspendEvents()
                For i As Integer = lstLayers.Items.Count - 1 To 0 Step -1
                    lstLayers.SelectedIndex = i
                    If ActiveLayer IsNot Nothing Then ActiveLayer.Save(IniFile, i)
                Next
                GISMap.Layers.ResumeEvents()
                .Save()
            End With
            'Else
            'If IsModified AndAlso GISMap.NumLayers > 1 Then
            '    Select Case SaveChangesWarning(FileName)
            '        Case Windows.Forms.DialogResult.Yes
            '            'save last layers used
            '            Dim IniFile As New clsWRDBIniFile
            '            With IniFile
            '                .SetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.NumGIS, CStr(lstLayers.Items.Count))
            '                For i As Integer = lstLayers.Items.Count - 1 To 0 Step -1
            '                    lstLayers.SelectedIndex = i
            '                    ActiveLayer.Save(IniFile, i)
            '                Next
            '                .SaveAndClose()
            '            End With
            '        Case Windows.Forms.DialogResult.No
            '        Case Windows.Forms.DialogResult.Cancel : Exit Function
            '    End Select
            'End If
            'End If
            IsModified = False
            Return True
        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        End Try
    End Function

    ''' <summary>
    ''' List of selected station IDs
    ''' </summary>
    Public Function SelectedStations() As Generic.List(Of String)
        Return lstStations
    End Function

    ''' <summary>
    ''' Return rectangle for area of interest (nothing if not specified)
    ''' </summary>
    Public Function AOIRectangle() As RectangleF
        Return _AOIRectangle
    End Function

    Private Sub AddLayer()
        Try
            With New OpenFileDialog
                .InitialDirectory = GetSetting(REGAPP, Me.Name, "AddLayer", My.Computer.FileSystem.SpecialDirectories.MyDocuments & "WRDB")
                .Filter = "ArcView Shape Files (*.shp)|*.shp"
                .AddExtension = True
                .CheckFileExists = True
                .DefaultExt = ".shp"
                .Multiselect = True
                .Title = "Select GIS Layer(s) to Add"
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    Dim text As String = ""
                    For Each s As String In .FileNames
                        Dim fn As String = ""
                        If WRDBShapefile IsNot Nothing AndAlso Not String.IsNullOrEmpty(WRDBShapefile.Filename) Then
                            fn = WRDBShapefile.Filename
                            WRDBShapefile.Close() 'in case need to reproject when add new layer
                        End If
                        ActiveLayer = clsLayer.Add(GISMap, s)
                        If ActiveLayer IsNot Nothing Then
                            text = ActiveLayer.Name
                            ActiveLayer.MoveBelowPolylineLayers()
                            If fn <> "" Then WRDBShapefile.Open(fn)
                            SaveSetting(REGAPP, Me.Name, "AddLayer", IO.Path.GetDirectoryName(s))
                        End If
                    Next
                    RefreshLayerList()
                    lstLayers.Text = text
                    If lstLayers.Items.Count = .FileNames.Length Then 'had no layer open before, so zoom extents
                        GISMap.ZoomToMaxExtent()
                    End If
                    .Dispose()
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub AnimTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles AnimTimer.Tick
        AnimTimer.Enabled = False
        IsCancelled = False
        With trkAnim
            If .Value = .Maximum Then
                .Value = .Minimum
            Else
                .Value = Math.Min(.Maximum, .Value + .SmallChange)
            End If
        End With
        If Not IsCancelled Then AnimTimer.Enabled = True
    End Sub

    Private Sub btnHelp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelp.Click
        mnuHelpContents.PerformClick()
    End Sub

    Private Sub btnLocation_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnLocation.Click
        LocatePlace()
    End Sub

    Private Sub btnWhatsThis_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnWhatsThis.Click
        mnuHelpWhatsThis.PerformClick()
    End Sub

    Private Sub cbo_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboDisplay.SelectedIndexChanged, cboPCode.SelectedIndexChanged, cboAggregate.SelectedIndexChanged
        Try
            If SuspendMapUpdate Then Exit Sub
            IsCancelled = False
            CType(sender, ToolStripComboBox).Enabled = False 'so don't change again before summarize is done
            If sender Is cboDisplay Then
                CheckDropDownItem(mnuDataDisplay, cboDisplay.SelectedIndex)
                SummarizeStationData()
            ElseIf sender Is cboAggregate Then
                tbAnim.Visible = cboAggregate.SelectedIndex <> 0
                mnuViewAnimate.Enabled = cboAggregate.SelectedIndex <> 0
                mnuViewAnimateSpeed.Enabled = cboAggregate.SelectedIndex <> 0

                CheckDropDownItem(mnuDataAggregate, cboAggregate.SelectedIndex)
                Aggregate = cboAggregate.SelectedIndex
                If Mode = enumMode.OutputData Then
                    SummarizeStationData()
                End If
            Else
                SummarizeStationData()
                CheckDropDownItem(mnuDataPCode, cboPCode.SelectedIndex)
            End If
            ResetAnimation()
            RefreshStationsDisplay()
            ProgressComplete()
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            CType(sender, ToolStripComboBox).Enabled = True
        End Try
    End Sub

    Friend Sub CopyMap(ByVal Map As DotSpatial.Controls.Map)
        Try
            Dim MyImage As New System.Drawing.Bitmap(Map.ClientRectangle.Width, Map.ClientRectangle.Height)
            Map.DrawToBitmap(MyImage, New Rectangle(0, 0, Map.ClientRectangle.Width, Map.ClientRectangle.Height))
            My.Computer.Clipboard.SetImage(MyImage)

            'Bitblasts a snapshot of the MapWindow map to the clipboard in VB Net
            'Dim g1 As System.Drawing.Graphics = AxMap1.CreateGraphics()
            'Dim MyImage As New System.Drawing.Bitmap(AxMap1.ClientRectangle.Width, AxMap1.ClientRectangle.Height, g1)
            'Dim g2 As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(MyImage)
            'Dim dc1 As IntPtr = g1.GetHdc()
            'Dim dc2 As IntPtr = g2.GetHdc()
            'Dim datobj As New System.Windows.Forms.DataObject
            'BitBlt(dc2, 0, 0, AxMap1.ClientRectangle.Width, AxMap1.ClientRectangle.Height, dc1, 0, 0, 13369376)
            'g1.ReleaseHdc(dc1)
            'g2.ReleaseHdc(dc2)
            'datobj.SetData(System.Windows.Forms.DataFormats.Bitmap, MyImage)
            'System.Windows.Forms.Clipboard.SetDataObject(datobj)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Called when user has executed a query in WRDB and it has completed: update thematic map with results by recreating a shp file
    ''' </summary>
    Private Sub CreateStationsLayer(Optional ByVal StationsList As Generic.List(Of String) = Nothing)
        Try
            If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()

            'if stations layer already exists, remove it
            For Each lyr As DotSpatial.Controls.IMapLayer In GISMap.Layers
                If lyr.LegendText = WRDBLayerName Then
                    GISMap.Layers.Remove(lyr)
                    Exit For
                End If
            Next

            WRDBShapefile = New DotSpatial.Data.PointShapefile

            If Mode = enumMode.DisplayStations AndAlso WRDBProject.IsStationShapefileValid Then
                ActiveLayer = clsLayer.Add(GISMap, WRDBShapeFileName)
                If ActiveLayer Is Nothing Then Exit Sub
                With CType(ActiveLayer, clsLayerPoint)
                    'let layer attributes be set by user
                    .Name = WRDBLayerName
                    'default stations display to circles for thematic mapping based on size; all others to triangle
                    Select Case Mode
                        Case enumMode.Animate, enumMode.OutputData
                            .PointShape = DotSpatial.Symbology.PointShape.Ellipse
                        Case Else
                            .PointShape = DotSpatial.Symbology.PointShape.Triangle
                            .PointAngle = 90
                    End Select
                    WRDBLayer = .Layer
                End With
                lstLayers.Text = WRDBLayerName
                WRDBShapefile.Open(WRDBShapeFileName)
                Exit Sub
            End If

            Try
                Dim dtStations As DataTable = Nothing
                If StationsList Is Nothing Then
                    Dim SQL As String = ""
                    Dim fieldList As String = "Station_ID as Station_ID,Station_Name as Stat_Name,Station_Type as Stat_Type,Latitude,Longitude"
                    If Not WRDBProject.GisQuickTable Then fieldList &= ",Data_Freq,Elevation,Branch_ID,Reach_ID,River_Mile,Dr_Area,Stations.Agency as Agency,Ref_ID,Remarks,Other,HUC"
                    With WRDBProject
                        If ModeSettings.SelectStationsFromDataTable Then
                            SQL = String.Format("SELECT DISTINCT Dta.{0} FROM ({1} Dta LEFT OUTER JOIN STATIONS ON Dta.Station_ID=Stations.Station_ID) WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList, ModeSettings.DataTableName)
                            .CopyPDoxSupportTables(.GetTableType(ActiveTableName), clsProject.enumTableTypes.Stations)
                        Else
                            SQL = String.Format("SELECT {0} FROM STATIONS WHERE Latitude IS NOT NULL AND Longitude IS NOT NULL", fieldList)
                        End If
                        WriteProgress("Loading data...")
                        If Not .DB.AddTable(dtStations, SQL) Then Exit Sub
                        ProgressComplete()
                    End With
                    dtStations.PrimaryKey = New DataColumn() {dtStations.Columns("Station_ID")}
                    If dtStations.Rows.Count = 0 Then Exit Sub
                Else
                    dtStations.Columns.Add("Station_ID", GetType(String))
                    dtStations.Columns.Add("Station_Name", GetType(String))
                    dtStations.Columns.Add("Station_Type", GetType(String))
                    dtStations.Columns.Add("Latitude", GetType(Double))
                    dtStations.Columns.Add("Longitude", GetType(Double))

                    WriteProgress("Loading data...")
                    For Each s As String In StationsList
                        dtStations.Rows.Add(s.Split(vbTab))
                        Application.DoEvents()
                    Next
                    ProgressComplete()
                    dtStations.PrimaryKey = New DataColumn() {dtStations.Columns("Station_ID")}
                    If dtStations.Rows.Count = 0 Then Exit Sub
                End If

                'add data so can do thematic mapping
                If dtResults IsNot Nothing Then
                    For i As Integer = 1 To dtResults.Columns.Count - 1
                        With dtResults.Columns(i)
                            'dtResult uses composite expressions so data types in table are usually Object; must explicitly map using name in SummarizeStationData
                            Dim dType As System.Type = GetType(Double)
                            If .ColumnName.ToUpper.Contains("NUM") Or .ColumnName.ToUpper.Contains("MONTH") Or .ColumnName.ToUpper.Contains("YEAR") Then
                                dType = GetType(Integer)
                            ElseIf .ColumnName.ToUpper.Contains("DATE") Then
                                dType = GetType(Date)
                            Else
                                dType = GetType(Double)
                            End If
                            dtStations.Columns.Add(.ColumnName, dType)
                        End With
                    Next

                    For Each row As DataRow In dtResults.Rows
                        Dim rowStations As DataRow = dtStations.Rows.Find(row.Item("Station_ID"))
                        If rowStations IsNot Nothing Then
                            For i As Integer = 1 To dtResults.Columns.Count - 1
                                Dim colName As String = dtResults.Columns(i).ColumnName
                                rowStations.Item(colName) = row.Item(colName)
                            Next
                        End If
                    Next
                End If

                'first remove all existing WRDB stations layers

                With GISMap
                    For i As Integer = .Layers.Count - 1 To 0 Step -1
                        If .Layers(i).LegendText.Equals(WRDBLayerName, StringComparison.OrdinalIgnoreCase) Then .Layers.RemoveAt(i) : Exit For
                    Next
                End With

                'delete existing shp files and create and add new one

                Dim NumShapes As Integer = 0

                WRDBShapefile = New DotSpatial.Data.PointShapefile()

                With WRDBShapefile
                    Dim fn As String = ""
                    Try
                        For i As Int16 = 1 To 5
                            Dim ext As String = Choose(i, "shp", "shx", "dbf", "prj", "mwsr")
                            fn = IO.Path.ChangeExtension(WRDBShapeFileName, ext)
                            If My.Computer.FileSystem.FileExists(fn) Then My.Computer.FileSystem.DeleteFile(fn)
                        Next
                    Catch ex As Exception
                        If My.Computer.FileSystem.FileExists(fn) Then
                            WarningMsg("Unable to delete file: {0}; it may be locked by another program.", fn)
                            Exit Sub
                        End If
                    End Try

                    If Not My.Computer.FileSystem.DirectoryExists(IO.Path.GetDirectoryName(WRDBShapeFileName)) Then My.Computer.FileSystem.CreateDirectory(IO.Path.GetDirectoryName(WRDBShapeFileName))

                    If WRDBShapefile IsNot Nothing Then

                        .IndexMode = True 'will add shapes and attributes independently

                        'add one field for each field in datatable
                        For i As Integer = 0 To dtStations.Columns.Count - 1
                            Dim c As DataColumn
                            With dtStations.Columns(i)
                                c = New DataColumn(.ColumnName, .DataType)
                            End With
                            .DataTable.Columns.Add(c)
                        Next

                        Dim rownum As Integer = 0

                        'if showing all station locations, create normal shape file with all stations; will add layer of normal points colored by station type
                        'if showing thematic map of values, shape file will contain only stations with data; will draw symbols of varying sizes

                        Dim minx As Double = Double.MaxValue, maxx As Double = -minx

                        For Each r As DataRow In dtStations.Rows
                            Dim x As Single = -Math.Abs(TestNull(r.Item("Longitude"), 0))
                            Dim y As Single = TestNull(r.Item("Latitude"), 0)
                            If Math.Abs(x) > 1 And y > 1 And Math.Abs(x) < 180 And y < 90 Then
                                minx = Math.Min(minx, x)
                                maxx = Math.Max(maxx, x)
                                Dim shp As New DotSpatial.Data.Shape(New DotSpatial.Topology.Coordinate(x, y))
                                Dim row As DataRow = .DataTable.NewRow
                                For c As Integer = 0 To .DataTable.Columns.Count - 1
                                    row.Item(c) = r.Item(c)
                                Next
                                .AddShape(shp)
                                .DataTable.Rows.Add(row)
                            End If
                            rownum += 1
                            WriteProgress(rownum, dtStations.Rows.Count - 1, "Creating WRDB stations shape file...")
                        Next

                        WriteProgress("Saving shape file...")

                        NumShapes = .DataTable.Rows.Count
                        .Projection = New DotSpatial.Projections.ProjectionInfo
                        .Projection.IsLatLon = True
                        .SaveAs(WRDBShapeFileName, True)
                        .Close()

                        If NumShapes = 0 Then WarningMsg("No stations have been added to the WRDB Stations layer because none of the selected data items referenced stations with valid latitude and longitude.")

                        'need to reproject to the current setting for the MW project 

                        If NumShapes > 0 And ProjectProjection IsNot Nothing AndAlso Not ProjectProjection.IsLatLon Then
                            Reproject(GISMap, WRDBShapeFileName, ProjectProjection, True)
                        End If
                    Else
                        'ErrorMsg(GISMap.get_ErrorMsg(GISMap.LastErrorCode))
                    End If

                    'shape file is created, now add to view

                    GISMap.Layers.SuspendEvents()

                    ActiveLayer = clsLayer.Add(GISMap, WRDBShapeFileName)
                    If ActiveLayer Is Nothing Then Exit Sub
                    With CType(ActiveLayer, clsLayerPoint)
                        .Name = WRDBLayerName
                        'default stations display to circles for thematic mapping based on size; all others to triangle
                        Select Case Mode
                            Case enumMode.Animate, enumMode.OutputData
                                .PointShape = DotSpatial.Symbology.PointShape.Ellipse
                                .PointSize = 20
                            Case Else
                                .PointShape = DotSpatial.Symbology.PointShape.Triangle
                                .PointSize = 12
                                .PointAngle = 90
                        End Select
                        WRDBLayer = .Layer
                    End With

                    'reopen so is available elsewhere
                    WRDBShapefile.Open(WRDBShapeFileName)

                End With
                dtStations.Dispose()

            Catch ex As Exception
                ErrorMsg(, ex)
            Finally
                'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
                ProgressComplete()
            End Try
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            If WRDBLayer IsNot Nothing Then
                'add station id labels
                'note: probably a bug...cannot set to different font
                Dim f As New Font("Tahoma", 8)
                GISMap.AddLabels(WRDBLayer, "[STATION_ID]", f, Color.Black)
                With WRDBLayer.LabelLayer.Symbolizer
                    .PreventCollisions = False
                    .OffsetY = 12
                    WRDBLayer.LabelLayer.IsVisible = False
                End With
            End If
            GISMap.Layers.ResumeEvents()
            RefreshLayerList()
        End Try
    End Sub

    Private Sub frmGIS_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
        If txtLocation.Focused And e.KeyChar = Chr(13) Then
            LocatePlace()
            e.Handled = True
        ElseIf e.KeyChar = Chr(27) Then
            IsCancelled = True
            With GISMap
                .ClearSelection(New DotSpatial.Topology.Envelope(.ViewExtents.X, .ViewExtents.Y, .ViewExtents.X + .ViewExtents.Width, .ViewExtents.Y + .ViewExtents.Height))
            End With
        End If
    End Sub

#Region "GISMap Event Handling"

    Private startext As DotSpatial.Data.Extent
    Private startx, starty As Double
    Private isPanning As Boolean = False

    Private Sub GISMap_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles GISMap.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Middle Then
            GISMap.FunctionMode = DotSpatial.Controls.FunctionMode.Pan
        End If
    End Sub

    Private Sub GISMap_ExtentsChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles GISMap.ViewExtentsChanged

        'always reset after a pan or zoom to select mode
        GISMap.FunctionMode = DotSpatial.Controls.FunctionMode.Select
        lstCoords.Clear() 'force recomputation of coordinates in FindPoints
        'IsModified = True
    End Sub

    Private Sub GISMap_MouseDownEvent(ByVal sender As Object, ByVal e As MouseEventArgs) 'Handles GISMap.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Right Then
            Me.ContextMenuMap.Show(Me, Me.PointToClient(Control.MousePosition))
        ElseIf Mode = enumMode.AreaOfInterest And AOIDrawingHandle <> -1 Then
            'todo: aoi drawing
            'GISMap.ClearDrawing(AOIDrawingHandle)
            AOIDrawingHandle = -1
        End If
    End Sub

    'Private Sub GISMap_MouseWheelDownEvent(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles GISMap.MouseWheelDownEvent
    '    With GISMap.PixelToProj(New Point(e.X, e.Y))
    '        startx = .X
    '        starty = .Y
    '    End With
    '    startext = GISMap.ViewExtents
    '    isPanning = True
    'End Sub

    'Private Sub GISMap_MouseWheelUpEvent(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles GISMap.MouseWheelUpEvent
    '    lstCoords.Clear() 'force recomputation of coordinates in FindPoints
    '    IsModified = True
    '    isPanning = False
    '    GISMap.Focus()
    'End Sub

    Private Sub GISMap_MouseMoveEvent(ByVal sender As Object, ByVal e As MouseEventArgs) Handles GISMap.MouseMove
        Static IsActive As Boolean = False
        If AnimationInfo.IsAnimating Then Exit Sub
        Try
            'If e.Button = 3 Then 'pressing wheel button
            '    With GISMap.PixelToProj(New Point(e.X, e.Y))
            '        Dim dx As Double = .X - startx, dy As Double = .Y - starty
            '        Dim ext As DotSpatial.Data.Extent = GISMap.ViewExtents
            '        With ext
            '            .SetValues(.MinX - dx, .MinY - dy, .MaxX - dx, .MaxY - dy)
            '        End With
            '        GISMap.ViewExtents = ext
            '        Exit Sub
            '    End With
            'End If

            Static lasttxt As String = ""
            If IsActive Then Exit Sub 'so will finish with one lookup before starts another
            IsActive = True

            'When mouse hovers over a valid shape, show the name of the GISTag on the status bar

            If IsSelecting Or ProjectProjection Is Nothing Then IsActive = False : Exit Sub

            If GISMap.FunctionMode <> DotSpatial.Controls.FunctionMode.Select Then IsActive = False : Exit Sub

            Dim pnt As DotSpatial.Topology.Coordinate = GISMap.PixelToProj(New Point(e.X, e.Y))

            Dim xy() As Double = {pnt.X, pnt.Y}
            Dim z() As Double = {0}
            DotSpatial.Projections.Reproject.ReprojectPoints(xy, z, ProjectProjection, DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984, 0, 1)

            If Not ProgressBar.Visible Then Me.StatusLabel.Text = String.Format("{0:N5},{1:N5}", xy(0), xy(1))

            Dim txt As String = ""

            If WRDBShapefile IsNot Nothing AndAlso ActiveLayer IsNot Nothing AndAlso ActiveLayer.Layer Is WRDBLayer Then
                For Each rec As Integer In FindShapes(e.X, e.Y)
                    With WRDBShapefile.DataTable.Rows(rec)
                        Select Case Mode
                            Case enumMode.OutputData
                                If dtResults Is Nothing Then IsActive = False : Exit Sub
                                Dim sel() As DataRow = dtResults.Select(String.Format("Station_ID='{0}'", CStr(.Item(enumField.ID).Replace("'", ""))))
                                For i As Integer = 0 To sel.Length - 1
                                    If (Mode <> enumMode.Animate And Mode <> enumMode.OutputData) Or sel(i).Item("MinDate") >= AnimationInfo.ThisDate Then
                                        txt &= String.Format("Station ID: {0}\n{1}\nNo. Data: {2}\nMin Value: {3:N3}\nMax Value: {4:N3}\nAvg Value: {5:N3}\nFirst Date: {6:d}\nLast Date: {7:d}\n------------------------------\n", _
                                        .Item(enumField.ID), .Item(enumField.Name), sel(i).Item("NumResult"), sel(i).Item("MinResult"), sel(i).Item("MaxResult"), sel(i).Item("AvgResult"), sel(i).Item("MinDate"), sel(i).Item("MaxDate")).Replace("\n", vbCrLf)
                                        Exit For
                                    End If
                                Next
                            Case enumMode.DisplayStations
                                If WRDBProject.GisQuickTable Then
                                    txt &= String.Format("Station ID: {0}\n{1}\n------------------------------\n", _
                                            .Item(enumField.ID), .Item(enumField.Name)).Replace("\n", vbCrLf)
                                Else
                                    txt &= String.Format("Station ID: {0}\n{1}\nType: {2}\nFreq: {3}\nElev: {4}\n------------------------------\n", _
                                            .Item(enumField.ID), .Item(enumField.Name), .Item(enumField.Type), .Item(enumField.Freq), .Item(enumField.Elev)).Replace("\n", vbCrLf)
                                End If
                            Case enumMode.SelectStations, enumMode.StationsList
                                txt &= String.Format("{0} - {1}", .Item(enumField.ID), .Item(enumField.Name))
                                ToolTip.AutomaticDelay = 200
                                If ToolTip.GetToolTip(GISMap) <> txt Then ToolTip.SetToolTip(GISMap, txt)
                                IsActive = False
                                Exit Sub
                            Case enumMode.Animate, enumMode.AreaOfInterest
                                'don't show anything
                        End Select
                    End With
                Next
                If Mode = enumMode.SelectStations Or Mode = enumMode.StationsList Then ToolTip.Hide(GISMap)
            Else
                'display info from non-stations layer
                If ActiveLayer IsNot Nothing AndAlso ActiveLayer.FileName <> "" Then
                    With ActiveLayer.FeatureSet
                        If .Features.Count <= 5000 Then
                            'WriteProgress("Searching for shape...")
                            For i As Integer = 0 To .Features.Count - 1
                                Dim rect As Rectangle = Nothing
                                If TypeOf .Features(i).BasicGeometry Is DotSpatial.Topology.Polygon AndAlso CType(.Features(i).BasicGeometry, DotSpatial.Topology.Polygon).Intersects(pnt(0), pnt(1)) Then
                                    For j As Integer = 0 To .DataTable.Columns.Count - 1
                                        txt &= String.Format("{0}: {1}" & vbCrLf, .DataTable.Columns(j).ColumnName, .Features(i).DataRow.Item(j))
                                    Next
                                    If ActiveLayer.Name = AnimationInfo.DataLayerName AndAlso AnimationInfo.CurrentResults IsNot Nothing Then
                                        With AnimationInfo
                                            Dim StaID As String = ActiveLayer.FeatureSet.DataTable.Rows(i).Item(.DataField)
                                            Dim value As Double = AnimationInfo.CurrentResults(StaID)
                                            txt &= String.Format("\nCurrent Data Value:\nDate-time: {0:MM/dd/yyyy}\nDisplay: {1}\nAggregation: {2}\nPCode: {3}\nValue: {4:N3}", .ThisDate, cboDisplay.Text, cboAggregate.Text, cboPCode.Text.Split(" ")(0), value).Replace("\n", vbCrLf)
                                        End With
                                    End If
                                    Exit For
                                End If
                            Next
                            'ProgressComplete()
                        End If
                    End With
                End If
            End If

            If txt <> lasttxt Then
                txtInfo.Text = txt
                lasttxt = txt
            End If

            IsActive = False

        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub GISMap_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs) Handles GISMap.SelectionChanged
        Try
            With GISMap
                If .FunctionMode = DotSpatial.Controls.FunctionMode.Select Then
                    If ModeSettings.AllowStationSelection AndAlso WRDBLayer IsNot Nothing AndAlso WRDBLayer.Selection IsNot Nothing Then
                        lstStations.Clear()
                        'RefreshStationsDisplay()
                        txtInfo.Text = "Selected Stations:"
                        For Each rownum As Integer In CType(WRDBLayer.Selection, DotSpatial.Symbology.IndexSelection)
                            Dim StationID As String = TestNull(WRDBLayer.DataSet.DataTable.Rows(rownum)(0), "")
                            Dim Stat_Name As String = TestNull(WRDBLayer.DataSet.DataTable.Rows(rownum)(1), "")
                            Dim Stat_Type As String = TestNull(WRDBLayer.DataSet.DataTable.Rows(rownum)(2), "")
                            If Mode <> enumMode.SelectStations Or cboDisplay.SelectedIndex = 0 Or cboDisplay.Text = Stat_Type Then
                                lstStations.Add(StationID)
                                txtInfo.Text &= vbCrLf & StationID & ": " & Stat_Name
                            End If
                        Next
                        If ModeSettings.AllowStationSelection Then btnSelect.Visible = lstStations.Count > 0
                    End If
                    'ElseIf Mode = enumMode.AreaOfInterest Then
                    '    If AOIDrawingHandle = -1 Then AOIDrawingHandle = .NewDrawing(MapWinGIS.tkDrawReferenceList.dlSpatiallyReferencedList)
                    '    GISMap.DrawLineEx(AOIDrawingHandle, pxMin, pyMin, pxMax, pyMin, 2, MapColor(Color.Red))
                    '    GISMap.DrawLineEx(AOIDrawingHandle, pxMax, pyMin, pxMax, pyMax, 2, MapColor(Color.Red))
                    '    GISMap.DrawLineEx(AOIDrawingHandle, pxMax, pyMax, pxMin, pyMax, 2, MapColor(Color.Red))
                    '    GISMap.DrawLineEx(AOIDrawingHandle, pxMin, pyMax, pxMin, pyMin, 2, MapColor(Color.Red))
                    '    'convert to lat/long
                    '    MapWinGeoProc.SpatialReference.ProjectPoint(pxMin, pyMin, ProjectProjection, LatLongProjection)
                    '    MapWinGeoProc.SpatialReference.ProjectPoint(pxMax, pyMax, ProjectProjection, LatLongProjection)
                    '    _AOIRectangle = New RectangleF(pxMin, pyMin, pxMax - pxMin, pyMax - pyMin)
                    '    btnSelect.Text = "Return Area of Interest"
                    '    btnSelect.Visible = True
                    'End If
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

#End Region

    ''' <summary>
    ''' Load settings from specified .GIS file
    ''' </summary>
    Private Sub LoadSettings(ByRef _FileName As String, IsAppend As Boolean)
        Try
            If Not My.Computer.FileSystem.FileExists(_FileName) Then Exit Sub

            IsCancelled = False

            If Not IsAppend Then
                If WRDBShapefile IsNot Nothing Then WRDBShapefile.Close()
                If AnimationInfo.dsData IsNot Nothing Then AnimationInfo.dsData.Dispose() : AnimationInfo.dsData = Nothing
                'If dtPolygon IsNot Nothing Then dtPolygon.Clear() : dtPolygon.Dispose() : dtPolygon = Nothing
                If dtResults IsNot Nothing Then dtResults.Clear() : dtResults.Dispose() : dtResults = Nothing
            End If

            GC.Collect()

            Dim IniFile As clsWRDBIniFile = Nothing
            IniFile = New clsWRDBIniFile(_FileName)

            GISMap.Layers.SuspendEvents()

            With IniFile
                If Not IsAppend Then
                    WRDBLayer = Nothing
                    WRDBShapefile = Nothing
                    GISMap.Layers.Clear()
                End If
                GISMap.Enabled = False
                GISMap.SuspendLayout()
                Dim NumGIS As Integer = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.NumGIS, "0"))
                Dim StaColor As Drawing.Color = Color.Red, StaType As DotSpatial.Symbology.PointShape = DotSpatial.Symbology.PointShape.Ellipse

                'form list of loaded files
                Dim lstExisting As New Generic.List(Of String)
                For i As Integer = 0 To GISMap.Layers.Count - 1
                    lstExisting.Add(GISMap.Layers(i).LegendText)
                Next

                For i As Integer = NumGIS - 1 To 0 Step -1
                    Dim FileName As String = .GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISShapeFile, "")
                    Dim LayerName As String = .GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISName, "")
                    WriteProgress(NumGIS - 1 - i, NumGIS - 1, "Loading {0}...", IO.Path.GetFileName(FileName))
                    If FileName.Trim = "" Then Continue For
                    If IsAppend AndAlso lstExisting.Contains(LayerName) Then Continue For 'skip loading if layername already exists

                    Dim FileNameRelative As String = .GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISShapeFileRelative, "")

                    'do not load the layer if it has already been loaded
                    If IO.Path.GetFileNameWithoutExtension(FileName).Equals(WRDBFileName, StringComparison.OrdinalIgnoreCase) Or
                       IO.Path.GetFileNameWithoutExtension(FileName).Equals(WRDBLayerName, StringComparison.OrdinalIgnoreCase) Then
                        StaColor = Drawing.Color.FromName(.GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISPointColor, "Red"))
                        If Not [Enum].TryParse(.GetKey("Layer_" & i, clsWRDBIniFile.enumKey.GISPointType, DotSpatial.Symbology.PointShape.Ellipse.ToString), StaType) Then StaType = DotSpatial.Symbology.PointShape.Ellipse
                    Else
                        FileName = FindFile(FileName, IO.Path.GetDirectoryName(.FileName), FileNameRelative)
                        If FileName <> "" Then
                            ActiveLayer = clsLayer.Add(GISMap, FileName)
                            If ActiveLayer Is Nothing Then Continue For
                            ActiveLayer.Load(IniFile, i)
                            'RefreshLayerList()
                        End If
                    End If
                    If IsCancelled Then Exit Sub
                Next

                'find WRDB Stations layer and always put it on top (last in layer list)
                For i As Integer = 0 To GISMap.Layers.Count - 1
                    If GISMap.Layers(i).LegendText = WRDBLayerName Then
                        clsLayer.MoveTop(GISMap, GISMap.Layers(i))
                        Exit For
                    End If
                Next

                GISMap.Layers.ResumeEvents()

                RefreshLayerList()
                If IniFile IsNot Nothing Then .Save()

                'If Mode <> enumMode.Animate And Mode <> enumMode.AreaOfInterest AndAlso My.Computer.FileSystem.FileExists(WRDBShapeFileName) Then 'add stations layer back

                '    Dim NewLayer As clsLayerPoint = clsLayer.Add(GISMap, WRDBShapeFileName)
                '    If NewLayer IsNot Nothing Then
                '        With NewLayer
                '            .Name = WRDBLayerName
                '            .PointSize = 12
                '            .FillColor = StaColor
                '            .PointShape = StaType
                '            .PointAngle = 90
                '            WRDBLayer = .Layer
                '            WRDBShapefile = .FeatureSet
                '        End With

                '        'reopen so is available elsewhere
                '        ''WRDBShapefile.Open(WRDBShapeFileName)
                '    End If
                'End If

                Dim xmin, xmax, ymin, ymax As Double
                xmin = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMin, "-1"))
                xmax = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_XMax, "-1"))
                ymin = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMin, "-1"))
                ymax = Val(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_YMax, "-1"))
                Dim ext As DotSpatial.Data.Extent = GISMap.ViewExtents
                If xmin <> -1 Then
                    ext.SetValues(xmin, ymin, xmax, ymax)
                    GISMap.ViewExtents = ext
                End If
                AnimationInfo.DataLayerName = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataLayer, "")
                AnimationInfo.DataField = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataField, "")
                Dim DataFile As String = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFile, "")
                Dim DataFileRelative As String = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataFileRelative, "")
                Dim lyrFound As Boolean = False
                For i As Integer = 0 To GISMap.Layers.Count - 1
                    If GISMap.Layers(i).LegendText.Equals(AnimationInfo.DataLayerName, StringComparison.OrdinalIgnoreCase) Then lyrFound = True : Exit For
                Next
                If Not lyrFound Then AnimationInfo.DataLayerName = ""
                If DataFile <> "" AndAlso AnimationInfo.DataLayerName <> "" AndAlso AnimationInfo.DataField <> "" AndAlso FindFile(DataFile, IO.Path.GetDirectoryName(.FileName), DataFileRelative) <> "" Then
                    AssignData(DataFile, AnimationInfo.DataLayerName, AnimationInfo.DataField)
                    If IsCancelled Then Exit Sub
                End If
                AnimationInfo.Colors(0) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor0, Color.LightGray.Name))
                AnimationInfo.Colors(1) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor1, Color.Green.Name))
                AnimationInfo.Colors(2) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor2, Color.Yellow.Name))
                AnimationInfo.Colors(3) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor3, Color.Red.Name))
                AnimationInfo.Colors(4) = Color.FromName(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataColor4, Color.White.Name))
                AnimationInfo.InvertColor = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataInvert, "False")
                AnimationInfo.ThresholdType = [Enum].Parse(GetType(enumThreshold), .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdType, enumThreshold.None.ToString))
                AnimationInfo.ThresholdValue = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DataThresholdValue, "0")

                SuspendMapUpdate = True

                lstLayers.SelectedIndex = IIf(lstLayers.Items.Count = 0, -1, 0)

                cboDisplay.Text = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Display, "Average Values")
                CheckDropDownItem(mnuDataDisplay, cboDisplay.SelectedIndex)

                cboPCode.Text = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_PCode, "")
                CheckDropDownItem(mnuDataPCode, cboPCode.SelectedIndex)

                cboAggregate.SelectedIndex = [Enum].Parse(GetType(enumAggregate), .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_Aggregate, enumAggregate.None.ToString))
                tbAnim.Visible = cboAggregate.SelectedIndex <> 0
                mnuViewAnimate.Enabled = cboAggregate.SelectedIndex <> 0
                mnuViewAnimateSpeed.Enabled = cboAggregate.SelectedIndex <> 0
                CheckDropDownItem(mnuDataAggregate, cboAggregate.SelectedIndex)
                Aggregate = cboAggregate.SelectedIndex

                AnimationInfo.ThisDate = CDate(.GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_DateTime, Date.MinValue.ToString))
                If AnimationInfo.ThisDate = Date.MinValue Then AnimationInfo.ThisDate = AnimationInfo.MinDate
                AnimationInfo.Speed = .GetKey(clsWRDBIniFile.enumSection.Application, clsWRDBIniFile.enumKey.GIS_AnimSpeed, "3")

                SuspendMapUpdate = False
                SummarizeStationData()
                ResetAnimation()
                RefreshStationsDisplay()
                GISMap.Enabled = True
                GISMap.Refresh()
            End With

        Catch ex As Exception
            ErrorMsg("An error occurred while trying to read the .GIS file.", ex)
        Finally
            GISMap.ResumeLayout()
            IsModified = False
            ProgressComplete()
        End Try
    End Sub

    Private Sub LocatePlace()
        Try
            If txtLocation.Text = "" Then Exit Sub
            With WRDBShapefile
                Dim found As Boolean = False
                For i As Integer = 0 To .Features.Count - 1
                    If .Features(i).DataRow(0) = txtLocation.Text.Trim.ToUpper Then
                        found = True
                        Dim x As Single = .Features(i).Coordinates(0).X
                        Dim y As Single = .Features(i).Coordinates(0).Y
                        Dim MyExtents As DotSpatial.Data.Extent = GISMap.ViewExtents
                        With MyExtents
                            Dim xy() As Double = {x, y}
                            Dim z() As Double = {0}
                            DotSpatial.Projections.Reproject.ReprojectPoints(xy, z, DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984, ProjectProjection, 0, 1)

                            .SetValues(xy(0) - .Width / 2, xy(1) - .Height / 2, xy(0) + .Width / 2, xy(1) + .Height / 2)
                            GISMap.ViewExtents = MyExtents
                        End With
                        Exit For
                    End If
                Next
                If Not found Then
                    WarningMsg("Station ID {0} was not found.", txtLocation.Text)
                End If
            End With
            txtLocation.Text = ""
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuClose.Click
        Close()
    End Sub

    Private Sub mnuDataAggregate_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles mnuDataAggregate.DropDownItemClicked
        Try
            Aggregate = mnuDataAggregate.DropDownItems.IndexOf(e.ClickedItem)
            tbAnim.Visible = Aggregate <> enumAggregate.POR
            mnuViewAnimate.Enabled = Aggregate <> enumAggregate.POR
            mnuViewAnimateSpeed.Enabled = Aggregate <> enumAggregate.POR
            CheckDropDownItem(mnuDataAggregate, e.ClickedItem)
            cboAggregate.Text = e.ClickedItem.Text.Replace("&", "")
            If Mode = enumMode.OutputData Then
                SummarizeStationData()
            Else
                ResetAnimation()
            End If
            RefreshStationsDisplay()
            ProgressComplete()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuDataDisplay_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles mnuDataDisplay.DropDownItemClicked
        Me.Refresh()
        Application.DoEvents()
        cboDisplay.Text = e.ClickedItem.Text
        CheckDropDownItem(mnuDataDisplay, e.ClickedItem)
    End Sub

    Private Sub mnuDataPCode_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles mnuDataPCode.DropDownItemClicked
        Application.DoEvents()
        cboPCode.Text = e.ClickedItem.Text
        CheckDropDownItem(mnuDataDisplay, e.ClickedItem)
    End Sub

    Private Sub mnuFileClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFileClose.Click, tbClose.Click
        Try
            With AnimationInfo
                If .dsData IsNot Nothing AndAlso WarningMsg("Are you sure you want to close the data table and detach it from the shape file?") = Windows.Forms.DialogResult.OK Then
                    'If dtPolygon IsNot Nothing Then dtPolygon.Clear() : dtPolygon.Dispose() : dtPolygon = Nothing
                    .dsData.Dispose()
                    .dsData = Nothing
                    .DataLayerName = ""
                    .DataField = ""
                    .DataHandle = -1
                    'reset data mapping properties on map layers (stored in key field)
                    'todo: note: couldn't find where this is used!
                    For i As Integer = 0 To GISMap.Layers.Count - 1
                        'GISMap.Layers(i). = Nothing
                    Next
                    'If .DataHandle <> -1 Then GISMap.set_LayerKey(.DataHandle, .dsData.FileName & ";" & .DataField)
                    Me.pgLayers.SelectedObject = ActiveLayer
                    picLegend.Visible = False
                    mnuViewLegend.Checked = False
                    GC.Collect()
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuFileNew_Click(sender As Object, e As System.EventArgs) Handles mnuFileNew.Click
        With GISMap
            If IsModified And GISMap.Layers.Count > 0 AndAlso Not (GISMap.Layers.Count = 1 AndAlso WRDBShapefile IsNot Nothing) Then
                Select Case SaveChangesWarning(GISFileName)
                    Case Windows.Forms.DialogResult.Yes : If Not SaveSettings(GISFileName) Then Exit Sub
                    Case Windows.Forms.DialogResult.No
                    Case Windows.Forms.DialogResult.Cancel : Exit Sub
                End Select
            End If
            WRDBLayer = Nothing
            WRDBShapefile = Nothing
            .Layers.Clear()
            RefreshLayerList()
            picLegend.Visible = False
            ProjectProjection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984
            GISFileName = ""
            ResetTitleBar()
        End With
    End Sub

    Private Sub mnuFileOpen_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileOpen.Click, tbOpen.Click
        Try
            If GISMap.Layers.Count = 0 Then
                WarningMsg("You cannot open a data file until you open a GIS polygon shape file to which the data will be mapped.")
                Exit Sub
            End If
            Dim FileName As String = ""
            With New OpenFileDialog
                .AddExtension = True
                .CheckFileExists = True
                .CheckPathExists = True
                .DefaultExt = ".bmd"
                .Filter = "All Data Formats|*.bmd;*.db;*.txt;*.csv;*.dbf;*.mdb;*.xls*;*.sdb;*.fdb;*.sdf;*.out;*.air|Binary Modeling Data Files (*.bmd)|*.bmd|Paradox Tables (*.db)|*.db|Tab-delimited Text Files (*.txt)|*.txt|Comma-separated Text Files (*.csv)|*.csv|dBase Tables (*.dbf)|*.dbf|Access Database Files (*.mdb)|*.mdb|Excel Spreadsheet Files (*.xls*)|*.xls*|Sqlite Database Files (*.sdb)|*.sdb|Firebird Database Files (*.fdb)|*.fdb|SQL Server CE Database Files (*.sdf)|*.sdf|LSPC Output Files (*.out)|*.out|LSPC Input Files (*.air)|*.air|EFDC Output Files (*.out)|*.out"
                .FilterIndex = GetSetting(REGAPP, Me.Name, "FileFilter", 0)
                .InitialDirectory = GetSetting(REGAPP, "General", "LastOpenDir", "")
                .Title = "Open Data File"
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    SaveSetting(REGAPP, Me.Name, "FileFilter", .FilterIndex)
                    If Not My.Computer.FileSystem.FileExists(.FileName) Then .Dispose() : Exit Sub
                    FileName = .FileName
                Else
                    .Dispose()
                    Exit Sub
                End If
            End With
            SaveSetting(REGAPP, "General", "LastOpenDir", IO.Path.GetDirectoryName(FileName))

            With AnimationInfo
                If Not AssignData(FileName, "", "") Then
                    .DataLayerName = ""
                    .DataField = ""
                    .DataHandle = -1
                    .dsData.Dispose()
                    .dsData = Nothing
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Open data source and set up linkages from Station ID to named field in polygon shape file; prepare polygon data table to receive data
    ''' </summary>
    ''' <param name="DataFileName">Name of data source file</param>
    ''' <param name="LayerName">Name of polygon layer</param>
    ''' <param name="FieldName">Name of field in polygon layer to which Station ID will be mapped</param>
    ''' <returns>True if successful</returns>
    Private Function AssignData(ByVal DataFileName As String, ByVal LayerName As String, ByVal FieldName As String) As Boolean
        Try
            With AnimationInfo
                WriteProgress("Initializing data file...")
                .dsData = WRDB.DataSource.clsData.Instantiate(DataFileName)
                If .dsData Is Nothing Then Return Nothing
                ProgressComplete()

                If Not (.dsData.ContainsFieldName("Station_ID") And .dsData.ContainsFieldName("Date_Time")) Then
                    WarningMsg("The selected data file does not contain the required fields: Station_ID and Date_Time.")
                    Return False
                End If

                Dim CheckShapes As Boolean = False

                If LayerName = "" Or FieldName = "" Then
                    With New frmSelectLayer(GISMap)
                        If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                            LayerName = .cboLayer.Text
                            FieldName = .cboField.Text
                            CheckShapes = True
                            .Dispose()
                        Else
                            .Dispose()
                            Return False
                        End If
                    End With
                End If

                .DataLayerName = LayerName
                .DataField = FieldName
                .DataHandle = GetLayerIndex(GISMap, .DataLayerName)

                'need to unload and reload this layer in case it is being assigned a new data table (attributes seem to get corrupted)
                Dim fn As String = CType(GISMap.Layers(.DataHandle), DotSpatial.Controls.MapPolygonLayer).DataSet.Filename
                GISMap.Layers.RemoveAt(.DataHandle)
                ActiveLayer = clsLayer.Add(GISMap, fn)

                cboPCode.Items.Clear()
                cboPCode.Items.AddRange(AnimationInfo.dsData.PCodes.ToArray)
                mnuDataPCode.DropDownItems.Clear()
                For Each s As String In AnimationInfo.dsData.PCodes
                    mnuDataPCode.DropDownItems.Add(s)
                    If mnuDataPCode.DropDownItems.Count > 50 Then Exit For
                Next
                'see what timestep is appropriate
                If .dsData.Read Then
                    Dim dt1 As Date = .dsData.Items("Date_Time")
                    If .dsData.Read Then
                        Dim dt2 As Date = .dsData.Items("Date_Time")
                        If dt2.Subtract(dt1).TotalHours <= 1 Then
                            .TimeUnits = enumTimeUnits.Minute
                            .TimeStep = dt2.Subtract(dt1).TotalMinutes
                        ElseIf dt2.Subtract(dt1).TotalDays <= 1 Then
                            .TimeUnits = enumTimeUnits.Hour
                            .TimeStep = dt2.Subtract(dt1).TotalHours
                        Else
                            .TimeUnits = enumTimeUnits.Day
                            .TimeStep = dt2.Subtract(dt1).TotalDays
                        End If
                    End If
                    .dsData.Rewind()
                End If

                'this is list of data stations that have associated shapes on data layer
                .StationList = New Generic.List(Of String)

                'get list of unique station IDs from data layer

                .DataLayer = GetLayer(GISMap, .DataLayerName)
                Dim fld As Integer = GetFieldIndex(.DataLayer.DataSet.DataTable, .DataField)
                For i As Integer = 0 To .DataLayer.DataSet.Features.Count - 1
                    Dim StaID As String = .DataLayer.DataSet.DataTable.Rows(i)(fld)
                    If Not .StationList.Contains(StaID) Then .StationList.Add(StaID)
                Next

                .StationList.Sort()

                'now, modify data feature layer data table to include field into which the values can be placed for thematic mapping

                If Not .DataLayer.DataSet.AttributesPopulated Then .DataLayer.DataSet.FillAttributes()
                fld = GetFieldIndex(.DataLayer.DataSet.DataTable, "Result")
                If fld = -1 Then 'not found, add it
                    .DataLayer.DataSet.DataTable.Columns.Add("Result", GetType(Double))
                End If
                .DataLayer.DataSet.DataTable.PrimaryKey = New DataColumn() {.DataLayer.DataSet.DataTable.Columns(.DataField)}

                If CheckShapes Then
                    'get list of unique station IDs and see how many matches there are to shape file (get date range)
                    .MinDate = .dsData.MinDate
                    .MaxDate = .dsData.MaxDate
                    Dim lstStaData As Generic.List(Of String) = .dsData.StationIDs

                    'find number of missing shapes
                    Dim NumMissingShapes As Integer = 0
                    For Each StaID As String In lstStaData
                        If Not .StationList.Contains(StaID) Then NumMissingShapes += 1
                    Next

                    'find number of missing data stations
                    Dim NumMissingStations As Integer = 0
                    For Each StaID As String In .StationList
                        If Not lstStaData.Contains(StaID) Then NumMissingStations += 1
                    Next

                    If NumMissingShapes + NumMissingStations > 0 Then WarningMsg("There are some mismatches between the data layer shapefile and available Station IDs in the data file: \n\n\t\b {0} out of the total of {1} unique Station IDs in the data file are missing from the shape file\n\n\t\b {2} out of the total of {3} shapes in the data layer had no associated data stations\n\nAs a result, some shapes in the data layer will not be visible during animation.", NumMissingShapes, lstStaData.Count, NumMissingStations, .StationList.Count)
                    'ResetTrackbar()
                    If cboPCode.Items.Count > 0 Then cboPCode.SelectedIndex = 0
                End If

                lstLayers.Text = .DataLayerName
                'reset data mapping properties on map layers (stored in key field)
                For i As Integer = 0 To GISMap.Layers.Count - 1
                    'GISMap.set_LayerKey(GISMap.get_LayerHandle(i), "")
                Next
                'If .DataHandle <> -1 Then GISMap.set_LayerKey(.DataHandle, .dsData.FileName & ";" & .DataField)
                ActiveLayer = clsLayer.Reset(GISMap, .DataLayerName)
                Me.pgLayers.SelectedObject = ActiveLayer
            End With
            picLegend.Visible = True
            mnuViewLegend.Checked = True
            Return True
        Catch ex As Exception
            ErrorMsg(, ex)
            Return False
        Finally
            ProgressComplete()
        End Try
    End Function

    Private Sub mnuFileOpenGroup_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileOpenGroup.Click, tbOpenGroup.Click, mnuFileAddGroup.Click
        Try
            Dim IsAppend As Boolean = sender Is mnuFileAddGroup

            'load last layers used
            If IsModified And Not IsAppend Then
                Select Case SaveChangesWarning(GISFileName)
                    Case Windows.Forms.DialogResult.Yes : If Not SaveSettings(GISFileName) Then Exit Sub
                    Case Windows.Forms.DialogResult.Cancel : Exit Sub
                End Select
            End If
            With New OpenFileDialog
                .AddExtension = True
                .CheckFileExists = True
                .CheckPathExists = True
                .DefaultExt = ".gis"
                .Filter = "WRDB GIS Files (*.gis)|*.gis"
                .FilterIndex = 0
                .Title = IIf(IsAppend, "Append", "Load") & " GIS File"
                .ValidateNames = True
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    If Not IsAppend Then GISFileName = .FileName
                    ResetTitleBar()
                    .Dispose()
                Else
                    Exit Sub
                    .Dispose()
                End If
                LoadSettings(.FileName, IsAppend)
            End With
            ResetAnimation()
            RefreshStationsDisplay()

            ' Call sub to add this file as an MRU
            If Not IsAppend Then mru.AddFile(GISFileName)
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuFileOpenLast_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileOpenLast.Click, tbOpenLast.Click
        Try
            If mnuFileRecent.DropDownItems.Count > 0 Then mnuFileRecent.DropDownItems(0).PerformClick()
            'LoadSettings(ModeSettings.LastSettingsFilename)
            'If Mode = enumMode.Animate Then SummarizePolygonData()
            'ResetAnimation()
            'ResetTrackbar()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuFileSaveGroup_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileSaveGroup.Click, tbSave.Click
        SaveSettings(GISFileName)
    End Sub

    Private Sub mnuFileSaveGroupAs_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileSaveGroupAs.Click, tbSaveAs.Click
        If SaveSettings(GISFileName, True) Then mru.AddFile(GISFileName)
    End Sub

    Private Sub mnuHelp_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuHelpAbout.Click, mnuHelpContents.Click, mnuHelpEMail.Click, mnuHelpWeb.Click, mnuHelpWhatsThis.Click
        Try
            Select Case mnuHelp.DropDownItems.IndexOf(sender)
                Case 0 : ShowHelp(Me, 24)
                Case 1 : VB6.WhatsThisMode(Me)
                Case 3 : Process.Start("http://www.wrdb.com")
                Case 4 : Process.Start("mailto:cwilson@wileng.com")
                Case 6
                    With New frmAbout
                        .ShowDialog(Me)
                        .Dispose()
                    End With
                    'VB6.ShowForm(frmAbout, VB6.FormShowConstants.Modal, Me)
                Case Else
                    WarningMsg("This is not yet implemented.")
            End Select
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuLayerAdd.Click, mnuLayerAdd2.Click, tbLayerAdd.Click
        Try
            AddLayer()
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerDown_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLayerDown.Click, mnuLayerDown2.Click, tbLayerDown.Click
        Try
            ActiveLayer.MoveDown()
            RefreshLayerList()
            'If lstLayers.SelectedIndex <> lstLayers.Items.Count - 1 Then lstLayers.SelectedIndex += 1
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerRemove_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuLayerRemove.Click, mnuLayerRemove2.Click, tbLayerRemove.Click
        Try
            ActiveLayer.Remove()
            RefreshLayerList()
            'if have removed all layers, reset project projection to lat/long
            If GISMap.Layers.Count = 0 Then ProjectProjection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerRemoveAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuLayerRemoveAll.Click, mnuLayerRemoveAll2.Click, tbRemoveAll.Click
        Try
            WRDBLayer = Nothing
            WRDBShapefile = Nothing
            GISMap.Layers.Clear()
            RefreshLayerList()
            'if have removed all layers, reset project projection to lat/long
            ProjectProjection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerTop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLayerTop.Click, mnuLayerTop2.Click, tbLayerTop.Click
        Try
            ActiveLayer.MoveTop()
            RefreshLayerList()
            lstLayers.SelectedIndex = 0
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLayerUp_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLayerUp.Click, mnuLayerUp2.Click, tbLayerUp.Click
        Try
            ActiveLayer.MoveUp()
            RefreshLayerList()
            'If lstLayers.SelectedIndex <> 0 Then lstLayers.SelectedIndex -= 1
            IsModified = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuLocate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuLocate.Click
        Try
            txtLocation.Text = InputBox("Enter the Station ID you wish to use as a new center point on the map:", "Locate", "")
            btnLocation.PerformClick()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuView_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuSelect.Click, mnuZoomWindow.Click, mnuZoomIn.Click, mnuZoomOut.Click, mnuZoomPrevious.Click, mnuZoomExtents.Click, mnuPan.Click, mnuCopyToClipboard.Click, mnuZoomWindow2.Click, mnuZoomIn2.Click, mnuZoomOut2.Click, mnuZoomPrevious2.Click, mnuZoomExtents2.Click, mnuPan2.Click, mnuCopyClipboard2.Click, tbSelect.Click, tbZoomWindow.Click, tbZoomIn.Click, tbZoomOut.Click, tbZoomPrevious.Click, tbZoomExtents.Click, tbPan.Click, tbCopy.Click
        Try
            With GISMap
                If sender Is mnuSelect Or sender Is mnuSelect2 Or sender Is tbSelect Then
                    .FunctionMode = DotSpatial.Controls.FunctionMode.Select
                ElseIf sender Is mnuZoomWindow Or sender Is mnuZoomWindow2 Or sender Is tbZoomWindow Then
                    .FunctionMode = DotSpatial.Controls.FunctionMode.ZoomIn
                ElseIf sender Is mnuZoomIn Or sender Is mnuZoomIn2 Or sender Is tbZoomIn Then
                    .ZoomIn()
                ElseIf sender Is mnuZoomOut Or sender Is mnuZoomOut2 Or sender Is tbZoomOut Then
                    .ZoomOut()
                ElseIf sender Is mnuZoomPrevious Or sender Is mnuZoomPrevious2 Or sender Is tbZoomPrevious Then
                    .ZoomToPrevious()
                ElseIf sender Is mnuZoomExtents Or sender Is mnuZoomExtents2 Or sender Is tbZoomExtents Then
                    ZoomExtents()
                ElseIf sender Is mnuPan Or sender Is mnuPan2 Or sender Is tbPan Then
                    .FunctionMode = DotSpatial.Controls.FunctionMode.Pan
                ElseIf sender Is mnuCopyToClipboard Or sender Is mnuCopyClipboard2 Or sender Is tbCopy Then
                    Application.DoEvents() 'to make context menu disappear
                    CopyMap(GISMap)
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewAnimate_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuViewAnimate.Click
        Try
            tbAnim.Visible = True
            tbAnimPlay.Checked = mnuViewAnimate.Checked
            AnimTimer.Enabled = tbAnimPlay.Checked
            AnimationInfo.IsAnimating = True
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewAttributes_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewAttributes.Click, tbAttrTable.Click
        Try
            With New frmTable
                .SetShapeFile(GetLayer(GISMap, lstLayers.Text).DataSet)
                .ShowDialog(Me)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewData_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuViewData.Click, tbDataTable.Click
        Try
            If AnimationInfo.dsData Is Nothing Then
                WarningMsg("You must open a data table before you can view it.")
                Exit Sub
            End If
            With New frmTable
                .SetDataSource(AnimationInfo.dsData)
                .ShowDialog(Me)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    'Private Sub trkAnim_VisibleChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles trkAnim.VisibleChanged
    '    If trkAnim.Visible Then ResetAnimationLimits()
    'End Sub

    ''' <summary>
    ''' Clear statusbar text and progress
    ''' </summary>
    Public Sub ProgressComplete()
        Try
            ProgressBar.Visible = False
            If Not AnimationInfo.IsAnimating Then StatusLabel.Text = ""
            StatusStripGIS.Refresh()
            Application.UseWaitCursor = False
            Application.DoEvents()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Function DisplayMode() As enumDisplay
        'Return cboDisplay.SelectedIndex + 1 'much faster
        If cboDisplay.Text Like "*No.*" Then
            Return enumDisplay.NumResult
        ElseIf cboDisplay.Text Like "*Minimum*" Then
            Return enumDisplay.MinResult
        ElseIf cboDisplay.Text Like "*Maximum*" Then
            Return enumDisplay.MaxResult
        ElseIf cboDisplay.Text Like "*Average*" Then
            Return enumDisplay.AvgResult
        Else
            Return enumDisplay.None
        End If
    End Function

    Private Sub pgLayers_GotFocus(sender As Object, e As System.EventArgs) Handles pgLayers.GotFocus
        With lstLayers
            ActiveLayer = clsLayer.Reset(GISMap, .Items.Count - 1 - .SelectedIndex)
        End With
    End Sub

    Private Sub pgLayers_PropertyValueChanged(ByVal s As Object, ByVal e As System.Windows.Forms.PropertyValueChangedEventArgs) Handles pgLayers.PropertyValueChanged
        RefreshStationsDisplay()
        IsModified = True
    End Sub

    ''' <summary>
    ''' Reset display attributes (colors and/or sizes) of station points
    ''' </summary>
    Private Sub RefreshStationsDisplay()
        Try
            If IsCancelled Or SuspendMapUpdate Then Exit Sub

            If WRDBLayer IsNot Nothing Then
                If Mode <> enumMode.Animate Then
                    If Not WRDBLayer.IsVisible OrElse (cboPCode.Visible And cboPCode.Text = "") Then Exit Sub
                    ActiveLayer = clsLayer.Reset(GISMap, WRDBLayerName)
                    WRDBLayer.IsVisible = True
                    If ActiveLayer IsNot Nothing Then mnuViewLabels.Checked = ActiveLayer.LabelVisible
                End If

            End If

            If AnimationInfo.dsData IsNot Nothing AndAlso AnimationInfo.DataLayer IsNot Nothing Then
                With GISMap
                    If mnuViewLabels.Checked Then
                        .CollisionDetection = True
                        .AddLabels(AnimationInfo.DataLayer, "", New Font("Arial", 8), Color.Black)
                    End If
                End With
            End If

            Dim WRDBPointLayer As clsLayerPoint = clsLayer.Reset(GISMap, WRDBLayerName)

            Select Case Mode
                Case enumMode.OutputData

                    If WRDBLayer IsNot Nothing And WRDBPointLayer IsNot Nothing Then

                        Dim minsize As Integer = 6
                        Dim maxsize As Integer = WRDBPointLayer.PointSize  'user-specified point size is max size

                        'CreateCategories does not seem to work automatically; create my own equally spaced categories manually
                        Dim NumCat As Integer = 20
                        Dim range, minrange As Double
                        If DisplayMode() = enumDisplay.NumResult Then
                            range = AnimationInfo.MaxNum - AnimationInfo.MinNum + 0.0001
                            minrange = AnimationInfo.MinNum
                        Else
                            range = AnimationInfo.MaxValue - AnimationInfo.MinValue + 0.0001
                            minrange = AnimationInfo.MinValue
                        End If
                        'set up symbology based on value
                        Dim pntScheme As New DotSpatial.Symbology.PointScheme
                        With pntScheme
                            .SuspendChangeEvent()
                            .SuspendEvents()
                            With .EditorSettings
                                .NumBreaks = NumCat
                                .ClassificationType = DotSpatial.Symbology.ClassificationType.Quantities
                                .IntervalMethod = DotSpatial.Symbology.IntervalMethod.EqualInterval
                                .FieldName = DisplayMode.ToString
                                .IntervalSnapMethod = DotSpatial.Symbology.IntervalSnapMethod.Rounding
                            End With
                            .ClearCategories()
                            For i As Integer = 0 To NumCat - 1
                                Dim minval As Double = minrange + i * range / NumCat
                                Dim maxval As Double = minval + range / NumCat
                                Dim pc As New DotSpatial.Symbology.PointCategory(WRDBPointLayer.FillColor, WRDBPointLayer.PointShape, WRDBPointLayer.PointSize)
                                pc.FilterExpression = String.Format("[{0}] >= {1} AND [{0}] < {2}", DisplayMode.ToString, minval, maxval) 'enum name is same as field name
                                Dim size As Double = minsize + i * 1.0 / (NumCat - 1) * (maxsize - minsize)
                                pc.Symbolizer.SetSize(New DotSpatial.Symbology.Size2D(size, size))
                                pc.Symbolizer.Smoothing = False
                                pc.Symbolizer.SetOutline(WRDBPointLayer.OutlineColor, 1)
                                pc.Symbolizer.Symbols(0).Angle = WRDBPointLayer.PointAngle
                                pc.SelectionSymbolizer.SetSize(pc.Symbolizer.GetSize)
                                .AddCategory(pc)
                            Next
                            .ResumeChangeEvent()
                            .ResumeEvents()
                        End With

                        'because the in-memory version of the datatable is getting modified, do not refresh from disk except first time

                        If WRDBLayer.DataSet.DataTable Is Nothing Then WRDBLayer.DataSet.FillAttributes()

                        'refresh in-memory data table with values from dtResults

                        dtResults.DefaultView.RowFilter = String.Format("MinDate>=#{0}# AND MinDate<#{1}#", AnimationInfo.ThisDate, AnimationInfo.NextDate)
                        dtResults.DefaultView.Sort = "Station_ID"

                        For Each row As DataRow In WRDBLayer.DataSet.DataTable.Rows 'loop through shapefile table
                            Dim rowNum As Integer = dtResults.DefaultView.Find(row(0)) 'see if can find in filtered view
                            For i As Integer = 1 To dtResults.Columns.Count - 1
                                Dim colName As String = dtResults.Columns(i).ColumnName
                                If rowNum = -1 Then
                                    row.Item(colName) = DBNull.Value
                                Else
                                    row.Item(colName) = dtResults.DefaultView(rowNum)(i)
                                End If
                            Next
                        Next


                        WRDBLayer.Symbology = pntScheme
                        WRDBLayer.ShowLabels = mnuViewLabels.Checked
                        GISMap.Refresh()

                    End If

                Case enumMode.DisplayStations, enumMode.SelectStations, enumMode.StationsList

                    If WRDBLayer IsNot Nothing Then
                        'set up categories for symbology base on distinct station type
                        Dim pntScheme As New DotSpatial.Symbology.PointScheme
                        pntScheme.ClearCategories()
                        For i As Integer = 1 To cboDisplay.Items.Count - 1
                            Dim ss As New DotSpatial.Symbology.SimpleSymbol(IIf(cboDisplay.Items.Count = 2, WRDBPointLayer.FillColor, DefaultLayerColor(i)), WRDBPointLayer.PointShape, WRDBPointLayer.PointSize)
                            ss.OutlineColor = WRDBPointLayer.OutlineColor
                            ss.OutlineWidth = 1
                            ss.Angle = WRDBPointLayer.PointAngle
                            Dim ps As New DotSpatial.Symbology.PointSymbolizer
                            ps.Symbols.Clear()
                            ps.Symbols.Add(ss)
                            Dim pc As New DotSpatial.Symbology.PointCategory(ps)
                            pc.FilterExpression = String.Format("[STAT_TYPE]='{0}'", cboDisplay.Items(i))
                            If cboDisplay.SelectedIndex = 0 Or cboDisplay.SelectedIndex = i Then pntScheme.AddCategory(pc)
                        Next
                        WRDBLayer.DataSet.FillAttributes()
                        WRDBLayer.Symbology = pntScheme
                        WRDBLayer.ShowLabels = mnuViewLabels.Checked
                    End If

                Case enumMode.Animate

                    'refill dtPolygon with data within time range

                    'With AnimationInfo
                    '    If .ThisDate <> Date.MinValue Then dtPolygon = .dsData.GetDataTable(.dsData.StationIDs, cboPCode.Text)
                    'End With

                    'If dtPolygon Is Nothing Then Exit Sub
                    Dim fld As Integer = -1
                    Dim hdl As Integer = -1

                    If Not String.IsNullOrEmpty(AnimationInfo.DataLayerName) Then
                        fld = GetFieldIndex(AnimationInfo.DataLayer.DataSet.DataTable, AnimationInfo.DataField)
                        hdl = GetLayerIndex(GISMap, AnimationInfo.DataLayerName)

                        'data table has all observations for all stations for selected pcode; set filter, find first record, and aggregate data
                        For r As Integer = 0 To AnimationInfo.DataLayer.DataSet.Features.Count - 1
                            With AnimationInfo.DataLayer.DataSet.DataTable.Rows(r)
                                Dim StaID As String = TestNull(.Item(fld), "")
                                Dim value As Double = -1
                                AnimationInfo.CurrentResults.TryGetValue(StaID, value)
                                .Item("Result") = IIf(value = -1, DBNull.Value, value)
                                If WRDBLayer IsNot Nothing Then
                                    If value >= 0 Then
                                        'todo: set shape fill color
                                        'GISMap.set_ShapeFillColor(hdl, r, ColorToUInt(GradientColor((value - AnimationInfo.MinValue) / (AnimationInfo.MaxValue - AnimationInfo.MinValue + 0.0001))))
                                        WRDBLayer.SetVisible(WRDBLayer.DataSet.Features(r), True)
                                    Else
                                        WRDBLayer.SetVisible(WRDBLayer.DataSet.Features(r), False)
                                    End If
                                End If
                                If IsCancelled Then Exit For
                                If Not AnimationInfo.IsAnimating Then
                                    If mnuViewLabels.Checked And value >= 0 Then
                                        'todo:labeling 
                                        'Dim x, y As Single
                                        'With sf.QuickExtents(r)
                                        '    x = (.xMin + .xMax) / 2
                                        '    y = (.yMin + .yMax) / 2
                                        'End With
                                        'GISMap.AddLabel(AnimationInfo.DataHandle, value.ToString("N3"), ColorToUInt(Color.Black), x, y, MapWinGIS.tkHJustification.hjCenter)
                                    End If
                                    WriteProgress(r, AnimationInfo.DataLayer.DataSet.Features.Count - 1, "Updating display...")
                                End If
                            End With
                        Next
                        ProgressComplete()
                        StatusLabel.Text = AnimationInfo.ThisDate.ToString(MyDateTimeFormat)
                    End If
            End Select

            If Mode = enumMode.Animate And AnimationInfo.DataLayer IsNot Nothing Then DrawLegend() Else GISMap.Refresh()
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            If Not tbAnim.Visible Then ProgressComplete()
            If Mode <> enumMode.Animate AndAlso WRDBLayer IsNot Nothing AndAlso WRDBLayer.DataSet.NumRows > 0 Then WRDBLayer.IsVisible = True
        End Try
    End Sub

    Friend Sub RefreshLayerList()
        Try
            With lstLayers
                Dim text As String = .Text
                .Items.Clear()
                pgLayers.SelectedObject = Nothing
                For i As Integer = GISMap.Layers.Count - 1 To 0 Step -1
                    .Items.Add(GISMap.Layers(i).LegendText)
                    .SetItemChecked(.Items.Count - 1, GISMap.Layers(i).IsVisible)
                Next
                .Text = text
                If .SelectedIndex = -1 And .Items.Count > 0 Then .SelectedIndex = 0
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Set title bar to include name of just opened or saved group file
    ''' </summary>
    Private Sub ResetTitleBar()
        If GISFileName = "" Then
            Text = "WRDB GIS 5.0"
        Else
            Text = IO.Path.GetFileName(GISFileName) & " - WRDB GIS 5.0"
        End If
    End Sub

    ''' <summary>
    ''' When trackbar changes, determine current date and next date for summary and store in AnimationInfo
    ''' </summary>
    Private Sub SetAnimationTimeStep()
        Try
            If Aggregate = enumAggregate.POR Then Exit Sub
            With AnimationInfo
                If .MinDate.Year = 9999 Then Exit Sub
                Select Case .TimeUnits
                    Case enumTimeUnits.Minute
                        .ThisDate = .MinDate.AddMinutes(trkAnim.Value)
                        .NextDate = .ThisDate.AddMinutes(.TimeStep)
                    Case enumTimeUnits.Hour
                        .ThisDate = .MinDate.AddHours(trkAnim.Value)
                        .NextDate = .ThisDate.AddHours(.TimeStep)
                    Case enumTimeUnits.Day
                        .ThisDate = .MinDate.AddDays(trkAnim.Value)
                        .NextDate = .ThisDate.AddDays(.TimeStep)
                    Case enumTimeUnits.Month
                        .ThisDate = New Date(.MinDate.Year + trkAnim.Value \ 12, trkAnim.Value Mod 12 + 1, 1)
                        .NextDate = .ThisDate.AddDays(DateTime.DaysInMonth(.ThisDate.Year, .ThisDate.Month))
                    Case enumTimeUnits.Year
                        .ThisDate = New Date(.MinDate.Year + trkAnim.Value, 1, 1)
                        .NextDate = .ThisDate.AddDays(IIf(DateTime.IsLeapYear(.ThisDate.Year), 366, 365))
                End Select
                trkAnim.Minimum = 0
                ToolTip.SetToolTip(trkAnim, .ThisDate.ToString(MyDateTimeFormat))
                StatusLabel.Text = .ThisDate.ToString(MyDateTimeFormat)
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ' ''' <summary>
    ' ''' Only call this when displaying data by pcode and have attached data file
    ' ''' </summary>
    ' ''' <remarks></remarks>
    'Friend Sub SummarizePolygonData()
    '    Try
    '        If IsCancelled Or AnimationInfo.dsData Is Nothing Then Exit Sub
    '        'If dtPolygon IsNot Nothing Then dtPolygon.Clear() : dtPolygon.Dispose() : dtPolygon = Nothing
    '        'GC.Collect()

    '        With AnimationInfo
    '            WriteProgress("Summarizing data...")

    '            RemoveHandler .dsData.Progress, AddressOf Progress
    '            AddHandler .dsData.Progress, AddressOf Progress

    '            .CurrentResults = .dsData.GetResults(AnimationInfo.StationList, cboPCode.Text, .ThisDate, .NextDate.AddSeconds(-1), DisplayMode)

    '            RemoveHandler .dsData.Progress, AddressOf Progress

    '            '.Rewind()

    '            ''assume that data are in increasing date-times and have standard column names

    '            'Dim iPcd As Integer = -1
    '            'For i As Integer = 0 To .PCodes.Count - 1
    '            '    If .PCodes(i).Equals(cboPCode.Text, StringComparison.OrdinalIgnoreCase) Then iPcd = i
    '            'Next
    '            'If iPcd = -1 Then
    '            '    WarningMsg("The data file is missing the column: " & cboPCode.Text)
    '            '    Exit Sub
    '            'End If

    '            ''add all observations for selected pcode into table

    '            'MenuMain.Enabled = False
    '            'tbMain.Enabled = False

    '            ''three methods are available here:
    '            ''1. original asyncronous method
    '            ''2. parallel processing method where data are accumulated into a dictionary of tables and then combined when all are done (2.5x faster)
    '            ''3. parallel processing method where final data table is locked and updated within the parallel loop (3.1x faster)
    '            ''Multipliers will go up in release mode and when more processors are available
    '            ''use type 2 as is nearly as fast and can report progress

    '            'Dim t1 As Date = Now

    '            'dtPolygon = .GetDataTable(.StationIDs, cboPCode.Text)

    '            'Dim t2 As Date = Now

    '            'Dim dictTables As New Concurrent.ConcurrentDictionary(Of String, DataTable)
    '            'Dim pcode As String = cboPCode.Text

    '            ''this is used to simulate performance differences when have a different number of processors
    '            ''testing in debug mode showed that even if setting is 1, get better performance (1.5x for type 2 above; type 3 is less affected)
    '            'Dim po As New Threading.Tasks.ParallelOptions
    '            'po.MaxDegreeOfParallelism = -1 '-1 for max number of processors, 1 for single, etc. (cannot be 0)

    '            'Threading.Tasks.Parallel.ForEach(.StationIDs, po, Sub(staID As String)
    '            '                                                      Dim ds As WRDB.DataSource.clsData = WRDB.DataSource.clsData.Instantiate(.FileName)
    '            '                                                      dictTables(staID) = ds.GetDataTable(staID, pcode)
    '            '                                                      ds.Dispose()
    '            '                                                  End Sub)

    '            ''compile all the tables in the dictionary into one multi-column table
    '            'dtPolygon = New DataTable
    '            'dtPolygon.Columns.Add("Date_Time", GetType(Date))
    '            'dtPolygon.PrimaryKey = New DataColumn() {dtPolygon.Columns(0)}
    '            'For Each staID As String In .StationIDs
    '            '    dtPolygon.Columns.Add(staID, GetType(Single))
    '            '    For i As Integer = 0 To dictTables(staID).Rows.Count - 1
    '            '        Dim staRow As DataRow = dictTables(staID).Rows(i)
    '            '        Dim Datetime As Date = staRow(0)
    '            '        Dim Result As Double = staRow(1)

    '            '        If True Then
    '            '            If dtPolygon.Columns.Count = 2 Then 'is first station so add date-time values
    '            '                dtPolygon.Rows.Add(Datetime, Result)
    '            '            Else
    '            '                If dtPolygon.Rows(i)(0) <> Datetime Then Throw New ApplicationException("While compiling data from " & .FileName & " the date-time values were unexpectedly different for station " & staID)
    '            '                dtPolygon.Rows(i).Item(dtPolygon.Columns.Count - 1) = Result
    '            '            End If
    '            '        Else
    '            '            Dim row As DataRow = dtPolygon.Rows.Find(Datetime)
    '            '            If row Is Nothing Then 'not found, add
    '            '                With dtPolygon.Rows.Add(Datetime)
    '            '                    .Item(dtPolygon.Columns.Count - 1) = Result
    '            '                End With
    '            '            Else 'update cross tab existing row
    '            '                With row
    '            '                    .Item(0) = Datetime
    '            '                    .Item(dtPolygon.Columns.Count - 1) = Result
    '            '                End With
    '            '            End If
    '            '        End If
    '            '    Next
    '            '    WriteProgress(.StationIDs.IndexOf(staID), .StationIDs.Count - 1, "Compiling data table: {0}", staID)
    '            'Next

    '            'Dim t3 As Date = Now

    '            ''try to do compilation in parallel

    '            'dictTables.Clear()

    '            'dtPolygon = New DataTable
    '            'dtPolygon.Columns.Add("Date_Time", GetType(Date))
    '            'dtPolygon.PrimaryKey = New DataColumn() {dtPolygon.Columns(0)}

    '            'Dim LockObj As New Object
    '            'Dim ctr As Integer = 0

    '            'WriteProgress("Compiling data...")

    '            'Threading.Tasks.Parallel.ForEach(.StationIDs, po, Sub(staID As String)
    '            '                                                      Dim ds As WRDB.DataSource.clsData = WRDB.DataSource.clsData.Instantiate(.FileName)
    '            '                                                      Dim dt As DataTable = ds.GetDataTable(staID, pcode)
    '            '                                                      ds.Dispose()
    '            '                                                      'compile all the tables in the dictionary into one multi-column table
    '            '                                                      SyncLock LockObj
    '            '                                                          dtPolygon.Columns.Add(staID, GetType(Single))
    '            '                                                          For i As Integer = 0 To dt.Rows.Count - 1
    '            '                                                              Dim staRow As DataRow = dt.Rows(i)
    '            '                                                              Dim Datetime As Date = staRow(0)
    '            '                                                              Dim Result As Double = staRow(1)
    '            '                                                              'this method assumes that data source has identical date-time values for each station (like BMD or LSPC)
    '            '                                                              If dtPolygon.Columns.Count = 2 Then 'is first station so add date-time values
    '            '                                                                  dtPolygon.Rows.Add(Datetime, Result)
    '            '                                                              Else
    '            '                                                                  If dtPolygon.Rows(i)(0) <> Datetime Then Throw New ApplicationException("While compiling data from " & .FileName & " the date-time values were unexpectedly different for station " & staID)
    '            '                                                                  dtPolygon.Rows(i).Item(dtPolygon.Columns.Count - 1) = Result
    '            '                                                              End If
    '            '                                                          Next
    '            '                                                          ctr += 1
    '            '                                                      End SyncLock
    '            '                                                      'can't get the delegate/invoke sequence to work--UI freezes up
    '            '                                                      'Dim ThisProgressDelegate As New ProgressDelegate(AddressOf Progress)
    '            '                                                      'Me.Invoke(ThisProgressDelegate, New Object() {ctr, .StationIDs.Count - 1, "Compiling data for "})
    '            '                                                      Application.DoEvents()
    '            '                                                  End Sub)


    '            'Dim t4 As Date = Now

    '            'MessageBox.Show(String.Format("Normal: {0:0.0}; Parallel: {1:0.0}; Really Parallel: {2:0.0}", t2.Subtract(t1).TotalSeconds, t3.Subtract(t2).TotalSeconds, t4.Subtract(t3).TotalSeconds))

    '            ''If dtPolygon.Rows.Count > 0 Then dtPolygon.PrimaryKey = New DataColumn() {dtPolygon.Columns("Station_ID"), dtPolygon.Columns("Date_Time")}

    '            'ResetAnimation()

    '        End With
    '    Catch ex As Exception
    '        ErrorMsg(, ex)
    '    Finally
    '        ProgressComplete()
    '        MenuMain.Enabled = True
    '        tbMain.Enabled = True
    '    End Try
    'End Sub

    ''' <summary>
    ''' Reset animation settings including timestep (depending on aggregation), min/max dates to be animated, min/max values over all times and stations for selected pcode, 
    ''' trackbar settings, and finally, redraw the animation legend; will also call ResetTrackbar
    ''' This is called whenever a file is loaded or the pcode, aggregation, or display type is changed
    ''' </summary>
    Private Sub ResetAnimation()
        Try
            If SuspendMapUpdate Or IsCancelled Then Exit Sub
            With AnimationInfo

                If .dsData Is Nothing Then Exit Sub

                Select Case Aggregate
                    Case enumAggregate.POR 'period of record; don't group by time but summarize for all data
                        .TimeUnits = enumTimeUnits.Day
                        .TimeStep = 1
                    Case enumAggregate.None
                        .TimeUnits = enumTimeUnits.Day
                        .TimeStep = 1
                        Dim dt As TimeSpan = New TimeSpan(1, 0, 0)
                        .dsData.Rewind()
                        If .dsData.Read Then
                            Dim t1 As Date = .dsData.Items(1)
                            If .dsData.Read Then
                                Dim t2 As Date = .dsData.Items(1)
                                dt = t2.Subtract(t1)
                            End If
                        End If
                        .dsData.Rewind()
                        If dt.TotalHours <= 1 Then
                            .TimeUnits = enumTimeUnits.Minute
                            .TimeStep = dt.TotalMinutes
                        ElseIf dt.TotalDays <= 1 Then
                            .TimeUnits = enumTimeUnits.Hour
                            .TimeStep = dt.TotalHours
                        Else
                            .TimeUnits = enumTimeUnits.Day
                            .TimeStep = dt.TotalDays
                        End If
                    Case enumAggregate.Month
                        .TimeUnits = enumTimeUnits.Month
                        .TimeStep = 1
                    Case enumAggregate.Growing 'need to recompute because SQL can't determine if in growing season
                        .TimeUnits = enumTimeUnits.Year
                        .TimeStep = 1
                    Case enumAggregate.Annual
                        .TimeUnits = enumTimeUnits.Year
                        .TimeStep = 1
                End Select
            End With

            'find min and max value for selected pcode for all station ids (only if station ID is in shape file) so can determine limits for trackbar and color range

            Dim staList As String = ""
            For Each StaID As String In AnimationInfo.StationList
                staList &= IIf(staList = "", "", ",") & "'" & StaID & "'"
            Next

            Dim MinDate As Date = Date.MaxValue
            Dim MaxDate As Date = Date.MinValue
            Dim MinValue As Double = Double.MaxValue, MaxValue As Double = Double.MinValue

            'If dtPolygon IsNot Nothing Then

            With AnimationInfo
                'MinDate = dtPolygon.Compute("MIN(Date_Time)", "")
                'MaxDate = dtPolygon.Compute("Max(Date_Time)", "")
                MinDate = .dsData.MinDate
                MaxDate = .dsData.MaxDate

                If Aggregate = enumAggregate.None Then
                    .MinDate = MinDate
                    .MaxDate = MaxDate
                Else
                    .MinDate = New Date(MinDate.Year, 1, 1)
                    .MaxDate = New Date(MaxDate.Year + 1, 1, 1)
                End If
                trkAnim.SmallChange = .TimeStep
                trkAnim.LargeChange = .TimeStep
                trkAnim.TickFrequency = .TimeStep
                trkAnim.Minimum = 0
                Select Case .TimeUnits
                    Case enumTimeUnits.Minute : trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalMinutes
                    Case enumTimeUnits.Hour : trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalHours
                    Case enumTimeUnits.Day : trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalDays
                    Case enumTimeUnits.Month : trkAnim.Maximum = (.MaxDate.Year - .MinDate.Year) * 12 - 1
                    Case enumTimeUnits.Year : trkAnim.Maximum = .MaxDate.Year - .MinDate.Year - 1
                End Select
                'If .ThisDate = Date.MinValue Then trkAnim.Value = trkAnim.Minimum
                trkAnim.Value = trkAnim.Minimum
                SetAnimationTimeStep()
                ResetTrackbar()

                'determine range of values for all stations and aggregated timesteps so can properly assign colors
                MinValue = Double.MaxValue
                MaxValue = Double.MinValue

                'For Each Staid As String In .StationList
                '    Dim dtMin, dtMax As Date
                '    dtMin = .MinDate
                '    Do
                '        Dim rowfilter As String = ""
                '        If Aggregate <> enumAggregate.POR And Aggregate <> enumAggregate.None Then
                '            Select Case .TimeUnits
                '                Case enumTimeUnits.Minute : dtMax = dtMin.AddMinutes(.TimeStep)
                '                Case enumTimeUnits.Hour : dtMax = dtMin.AddHours(.TimeStep)
                '                Case enumTimeUnits.Day : dtMax = dtMin.AddDays(.TimeStep)
                '                Case enumTimeUnits.Month : dtMax = dtMin.AddDays(DateTime.DaysInMonth(dtMin.Year, dtMin.Month))
                '                Case enumTimeUnits.Year : dtMax = dtMin.AddDays(IIf(DateTime.IsLeapYear(dtMin.Year), 366, 365))
                '            End Select
                '            rowfilter = String.Format("Date_Time>=#{0}# AND Date_Time<#{1}#", dtMin, dtMax)
                '        End If

                '        Dim value As Double = -1

                '        If Aggregate = enumAggregate.None Then
                '            Select Case DisplayMode()
                '                Case enumDisplay.NumResult : value = 1
                '                Case Else : value = TestNull(dtPolygon.Select(rowfilter)(0)(1), -1)
                '            End Select
                '        Else
                '            Dim aggFunc As String = ""
                '            Select Case DisplayMode()
                '                Case enumDisplay.NumResult : aggFunc = "Count"
                '                Case enumDisplay.MinResult : aggFunc = "Min"
                '                Case enumDisplay.MaxResult : aggFunc = "Max"
                '                Case enumDisplay.AvgResult : aggFunc = "Avg"
                '            End Select
                '            If dtPolygon.Columns.Contains(Staid) Then value = TestNull(dtPolygon.Compute(String.Format("{0}([{1}])", aggFunc, Staid), rowfilter), -1)
                '        End If

                '        If .DataLayer IsNot Nothing AndAlso value <> -1 Then
                '            MinValue = Math.Min(MinValue, value)
                '            MaxValue = Math.Max(MaxValue, value)
                '            'place value in Result field in data polygon shapefile field
                '            Dim polyRow As DataRow = .DataLayer.DataSet.DataTable.Rows.Find(Staid)
                '            If polyRow IsNot Nothing Then polyRow("Result") = value
                '        End If

                '        dtMin = dtMax
                '    Loop Until dtMin > .MaxDate Or Aggregate = enumAggregate.POR Or Aggregate = enumAggregate.None
                '    WriteProgress(.StationList.IndexOf(Staid), .StationList.Count - 1, "Summarizing data...")
                'Next

                ''determine range of values for all stations and dates
                'For Each Staid As String In .StationList

                '    Dim dtMin, dtMax As Date
                '    dtMin = .MinDate
                '    Do
                '        If Aggregate <> enumAggregate.POR And Aggregate <> enumAggregate.None Then
                '            Select Case .TimeUnits
                '                Case enumTimeUnits.Minute : dtMax = dtMin.AddMinutes(.TimeStep)
                '                Case enumTimeUnits.Hour : dtMax = dtMin.AddHours(.TimeStep)
                '                Case enumTimeUnits.Day : dtMax = dtMin.AddDays(.TimeStep)
                '                Case enumTimeUnits.Month : dtMax = dtMin.AddDays(DateTime.DaysInMonth(dtMin.Year, dtMin.Month))
                '                Case enumTimeUnits.Year : dtMax = dtMin.AddDays(IIf(DateTime.IsLeapYear(dtMin.Year), 366, 365))
                '            End Select
                '        Else
                '            dtMax = .MaxDate
                '        End If

                '        Dim value As Double = .dsData.GetResult(Staid, cboPCode.Text, dtMin, dtMax, DisplayMode)

                '        If .DataLayer IsNot Nothing AndAlso value <> -1 Then
                '            MinValue = Math.Min(MinValue, value)
                '            MaxValue = Math.Max(MaxValue, value)
                '            ''place value in Result field in data polygon shapefile field
                '            'Dim polyRow As DataRow = .DataLayer.DataSet.DataTable.Rows.Find(Staid)
                '            'If polyRow IsNot Nothing Then polyRow("Result") = value
                '        End If

                '        dtMin = dtMax
                '    Loop Until dtMin > .MaxDate Or Aggregate = enumAggregate.POR Or Aggregate = enumAggregate.None
                '    WriteProgress(.StationList.IndexOf(Staid), .StationList.Count - 1, "Summarizing data...")
                'Next


                'determine range of values for all stations and dates
                Dim dictMinResults As Dictionary(Of String, Single) = .dsData.GetResults(.StationList, cboPCode.Text, .MinDate, .MaxDate, DataSource.clsData.enumAggregateFunction.Minimum)
                Dim dictMaxResults As Dictionary(Of String, Single) = .dsData.GetResults(.StationList, cboPCode.Text, .MinDate, .MaxDate, DataSource.clsData.enumAggregateFunction.Maximum)
                For Each Staid As String In .StationList
                    MinValue = Math.Min(MinValue, dictMinResults(Staid))
                    MaxValue = Math.Max(MaxValue, dictMaxResults(Staid))
                    WriteProgress(.StationList.IndexOf(Staid), .StationList.Count - 1, "Summarizing data...")
                Next

                .MinValue = MinValue
                .MaxValue = MaxValue

                If Aggregate = enumAggregate.POR Then
                    .ThisDate = .MinDate
                    .NextDate = .MaxDate
                End If
            End With
            'End If
            DrawLegend()
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

    ''' <summary>
    ''' Only call this when displaying data by pcode
    ''' </summary>
    ''' <remarks>When first create Stations shape file, this is called first to set up dtResults; thereafter when PCode changes, this is called again to update the fields in the attribute table</remarks>
    Friend Sub SummarizeStationData()
        Try
            With WRDBProject
                'If MainForm IsNot Nothing Then MainForm.WriteProgress("Summarizing data...", ProgressBarStyle.Marquee)
                WriteProgress("Summarizing data...")
                'query all stations in support table (or referenced in active working/master or temp table) 
                Select Case Mode
                    Case enumMode.OutputData
                        Dim sqlGroup As String = ""
                        Dim sqlWhere As String = ""
                        With AnimationInfo
                            .TimeUnits = enumTimeUnits.Day
                            .TimeStep = 1
                        End With
                        trkAnim.Enabled = True
                        tbAnim.Enabled = True

                        Select Case Aggregate
                            Case enumAggregate.POR 'period of record; don't group by time but summarize for all data
                                trkAnim.Enabled = False
                                tbAnim.Enabled = False
                                With AnimationInfo
                                    .TimeUnits = enumTimeUnits.Year
                                    .ThisDate = Date.MinValue '.MinDate
                                    .NextDate = Date.MaxValue '.MaxDate
                                End With

                            Case enumAggregate.None
                                sqlGroup = ", Date_Time"
                            Case enumAggregate.Month
                                Select Case .DB.DatabaseType
                                    Case clsDB.enumDBType.Access, clsDB.enumDBType.Paradox, clsDB.enumDBType.DBase, clsDB.enumDBType.Excel, clsDB.enumDBType.Text, clsDB.enumDBType.SQL_Server, clsDB.enumDBType.SQL_Server_CE
                                        sqlGroup = ", MONTH(Date_Time), YEAR(Date_Time)"
                                    Case clsDB.enumDBType.SQLite
                                        sqlGroup = ", STRFTIME('%m',Date_Time), STRFTIME('%Y',Date_Time)"
                                    Case Else
                                        sqlGroup = ", EXTRACT(MONTH from Date_Time), EXTRACT(YEAR from Date_Time)"
                                End Select
                                AnimationInfo.TimeUnits = enumTimeUnits.Month
                            Case enumAggregate.Growing
                                Select Case .DB.DatabaseType
                                    Case clsDB.enumDBType.Access, clsDB.enumDBType.Paradox, clsDB.enumDBType.DBase, clsDB.enumDBType.Excel, clsDB.enumDBType.Text, clsDB.enumDBType.SQL_Server, clsDB.enumDBType.SQL_Server_CE
                                        sqlGroup = ", YEAR(Date_Time)"
                                        sqlWhere = String.Format(" AND MONTH(Date_Time) * 100 + DAY(Date_Time) >= {0} AND MONTH(Date_Time) * 100 + DAY(Date_Time) <= {1}", GrowingMonth1 * 100 + GrowingDay1, GrowingMonth2 * 100 + GrowingDay2)
                                    Case clsDB.enumDBType.SQLite
                                        sqlGroup = ", STRFTIME('%Y',Date_Time)"
                                        sqlWhere = String.Format(" AND STRFTIME('%m',Date_Time) * 100 + STRFTIME('%D',Date_Time) >= {0} AND STRFTIME('%m',Date_Time) * 100 + STRFTIME('%D',Date_Time) <= {1}", GrowingMonth1 * 100 + GrowingDay1, GrowingMonth2 * 100 + GrowingDay2)
                                    Case Else
                                        sqlGroup = ", EXTRACT(YEAR from Date_Time) as Year"
                                        sqlWhere = String.Format(" AND EXTRACT(MONTH FROM Date_Time) * 100 + EXTRACT(DAY FROM Date_Time) >= {0} AND EXTRACT(MONTH FROM Date_Time) * 100 + EXTRACT(DAY FROM Date_Time) <= {1}", GrowingMonth1 * 100 + GrowingDay1, GrowingMonth2 * 100 + GrowingDay2)
                                End Select
                                AnimationInfo.TimeUnits = enumTimeUnits.Year
                            Case enumAggregate.Annual
                                Select Case .DB.DatabaseType
                                    Case clsDB.enumDBType.Access, clsDB.enumDBType.Paradox, clsDB.enumDBType.DBase, clsDB.enumDBType.Excel, clsDB.enumDBType.Text, clsDB.enumDBType.SQL_Server, clsDB.enumDBType.SQL_Server_CE
                                        sqlGroup = ", YEAR(Date_Time)"
                                    Case clsDB.enumDBType.SQLite
                                        sqlGroup = ", STRFTIME('%Y',Date_Time)"
                                    Case Else
                                        sqlGroup = ", EXTRACT(YEAR from Date_Time)"
                                End Select
                                AnimationInfo.TimeUnits = enumTimeUnits.Year
                        End Select

                        Dim Sql As String = String.Format( _
                            "SELECT Station_ID, " & _
                            "COUNT(Result) as NumResult, " & _
                            "MIN(Result) as MinResult, " & _
                            "MAX(Result) as MaxResult, " & _
                            "AVG(Result) as AvgResult, " & _
                            "MIN(Date_Time) as MinDate, " & _
                            "MAX(Date_Time) as MaxDate " & _
                            "FROM {0} WHERE Station_ID IS NOT NULL AND PCode='{1}'{2} GROUP BY Station_ID{3}", TempData, cboPCode.Text, sqlWhere, sqlGroup)

                        'need to disable to dont get open data reader error
                        tbAnimNext.Enabled = False
                        tbAnimPrev.Enabled = False
                        tbAnimBeg.Enabled = False
                        tbAnimEnd.Enabled = False
                        trkAnim.Enabled = False

                        If Not .DB.AddTable(dtResults, Sql) Then Exit Sub

                        tbAnimNext.Enabled = True
                        tbAnimPrev.Enabled = True
                        tbAnimBeg.Enabled = True
                        tbAnimEnd.Enabled = True
                        trkAnim.Enabled = True

                        dtResults.PrimaryKey = New DataColumn() {dtResults.Columns("Station_ID"), dtResults.Columns("MinDate")}

                        If WRDBLayer IsNot Nothing Then
                            'refresh previously created shape file with updated data
                            WRDBLayer.DataSet.DataTable.PrimaryKey = New DataColumn() {WRDBLayer.DataSet.DataTable.Columns("Station_ID")}
                            For Each row As DataRow In dtResults.Rows
                                Dim rowStations As DataRow = WRDBLayer.DataSet.DataTable.Rows.Find(row.Item("Station_ID"))
                                If rowStations IsNot Nothing Then
                                    For i As Integer = 1 To dtResults.Columns.Count - 1
                                        Dim colName As String = dtResults.Columns(i).ColumnName
                                        rowStations.Item(colName) = row.Item(colName)
                                    Next
                                End If
                            Next
                        End If

                        Dim staList As String = ""

                        ''find min and max value for selected pcode (only if station ID is in shape file)
                        'Dim sb As New System.Text.StringBuilder
                        'For r As Integer = 0 To WRDBShapefile.Features.Count - 1
                        '    If sb.Length <> 0 Then sb.Append(",")
                        '    sb.Append(String.Format("'{0}'", WRDBShapefile.DataTable.Rows(r)(0)))
                        'Next
                        'Dim staList As String = sb.ToString
                        'Dim staList As String = ""
                        'For r As Integer = 0 To WRDBShapefile.NumShapes - 1
                        '    staList &= IIf(staList = "", "", ",") & "'" & WRDBShapefile.CellValue(0, r) & "'"
                        'Next

                        Dim MinNum, MaxNum As Integer
                        Dim MinValue, MaxValue As Double
                        Dim MinDate, MaxDate As Date

                        'With dtResults
                        '    Dim rowfilter As String = ""
                        '    If staList <> "" Then rowfilter = String.Format("Station_ID IN ({0})", staList)
                        '    MinDate = TestNull(.Compute("Min(MinDate)", rowfilter), Now) : Application.DoEvents()
                        '    MaxDate = TestNull(.Compute("Max(MinDate)", rowfilter), Now) : Application.DoEvents()
                        '    MinValue = TestNull(.Compute("Min(MinResult)", rowfilter), 0) : Application.DoEvents()
                        '    MaxValue = TestNull(.Compute("Max(MaxResult)", rowfilter), 0) : Application.DoEvents()
                        '    MinNum = TestNull(.Compute("Min(NumResult)", rowfilter), 0) : Application.DoEvents()
                        '    MaxNum = TestNull(.Compute("Max(NumResult)", rowfilter), 0) : Application.DoEvents()
                        'End With

                        'sqlite puts all but first column in datatable as objects, so .Compute fails

                        With dtResults.DefaultView
                            Dim rowfilter As String = ""
                            If staList <> "" Then rowfilter = String.Format("Station_ID IN ({0})", staList)
                            If rowfilter.Length < 5000 Then .RowFilter = rowfilter
                            If .Count > 0 Then
                                .Sort = "NumResult"
                                MinNum = .Item(0).Item("NumResult")
                                MaxNum = .Item(.Count - 1).Item("NumResult")
                                .Sort = "MinDate"
                                MinDate = .Item(0)("MinDate")
                                .Sort = "MaxDate"
                                MaxDate = .Item(.Count - 1)("MaxDate")
                                .Sort = "MinResult"
                                MinValue = .Item(0)("MinResult")
                                .Sort = "MaxResult"
                                MaxValue = .Item(.Count - 1)("MaxResult")
                            End If
                        End With

                        With AnimationInfo
                            .MinDate = New Date(MinDate.Year, 1, 1)
                            .MaxDate = New Date(MaxDate.Year + 1, 1, 1)
                            .MinValue = MinValue
                            .MaxValue = MaxValue
                            .MinNum = MinNum
                            .MaxNum = MaxNum

                            trkAnim.SmallChange = .TimeStep
                            trkAnim.LargeChange = .TimeStep
                            If .ThisDate = Date.MinValue Then trkAnim.Value = 0
                            trkAnim.Minimum = 0
                            Select Case .TimeUnits
                                Case enumTimeUnits.Minute
                                    trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalMinutes
                                Case enumTimeUnits.Hour
                                    trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalHours
                                Case enumTimeUnits.Day
                                    trkAnim.Maximum = .MaxDate.Subtract(.MinDate).TotalDays
                                Case enumTimeUnits.Month
                                    trkAnim.Maximum = (.MaxDate.Year - .MinDate.Year) * 12 - 1
                                Case enumTimeUnits.Year
                                    trkAnim.Maximum = .MaxDate.Year - .MinDate.Year - 1
                            End Select
                            'trkAnim.Value = 0
                            SetAnimationTimeStep()
                        End With
                End Select
                'If AnimationInfo.dsData IsNot Nothing Then SummarizePolygonData()
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            'If MainForm IsNot Nothing Then MainForm.ProgressComplete()
            ProgressComplete()
        End Try
    End Sub

    Private Sub tbAnim_ItemClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles tbAnim.ItemClicked
        Try
            With trkAnim
                If e.ClickedItem Is tbAnimBeg Then
                    tbAnimPause.PerformClick()
                    .Value = .Minimum
                ElseIf e.ClickedItem Is tbAnimEnd Then
                    tbAnimPause.PerformClick()
                    .Value = .Maximum
                ElseIf e.ClickedItem Is tbAnimNext Then
                    tbAnimPause.PerformClick()
                    If .Value + .SmallChange > .Maximum Then
                        .Value = .Minimum
                    Else
                        .Value += .SmallChange
                    End If
                ElseIf e.ClickedItem Is tbAnimPrev Then
                    tbAnimPause.PerformClick()
                    If .Value - .SmallChange < .Minimum Then
                        .Value = .Maximum
                    Else
                        .Value -= .SmallChange
                    End If
                ElseIf e.ClickedItem Is tbAnimPause Then
                    AnimTimer.Enabled = False
                    IsCancelled = True
                    tbAnimPlay.Checked = False
                    mnuViewAnimate.Checked = False
                    AnimationInfo.IsAnimating = False
                ElseIf e.ClickedItem Is tbAnimPlay Then
                    AnimTimer.Enabled = Not tbAnimPlay.Checked
                    AnimationInfo.IsAnimating = Not tbAnimPlay.Checked
                    IsCancelled = Not AnimTimer.Enabled
                    mnuViewAnimate.Checked = Not tbAnimPlay.Checked
                ElseIf e.ClickedItem Is tbAnimClose Then
                    tbAnimPause.PerformClick()
                    tbAnim.Visible = False
                End If
            End With
            ToolTip.Active = Not AnimTimer.Enabled
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub tbAnim_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles tbAnim.Paint
        With trkAnim
            .Left = tbAnim.Left + tbAnimBeg.Width * 0.2
            .Top = tbAnim.Top + tbAnim.Height * 0.1
            .Height = tbAnim.Height * 0.8
            .Width = tbAnim.Width - tbAnimBeg.Width * 9
            .BringToFront()
        End With
    End Sub

    Private Sub tbAnim_VisibleChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tbAnim.VisibleChanged
        trkAnim.Visible = tbAnim.Visible
        'mnuToolbarAnim.Checked = tbAnim.Visible
        'mnuTbAnim.Checked = tbAnim.Visible
    End Sub

    Private Sub tbAnimSpeed_DropDownItemClicked(ByVal sender As Object, ByVal e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles tbAnimSpeed.DropDownItemClicked, mnuViewAnimateSpeed.DropDownItemClicked
        Dim Index As Integer = sender.DropDownItems.IndexOf(e.ClickedItem)
        AnimationInfo.Speed = Index + 1
        AnimTimer.Interval = 1 + (4 - Index) * 100 'will range from 1 to 401 milliseconds delay
        CheckDropDownItem(mnuViewAnimateSpeed, Index)
        CheckDropDownItem(tbAnimSpeed, Index)
    End Sub

    Private Sub trkAnim_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles trkAnim.MouseDown
        tbAnimPause.PerformClick()
    End Sub

    Private Sub trkAnim_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles trkAnim.ValueChanged
        IsCancelled = False
        ResetTrackbar()
    End Sub

    ''' <summary>
    ''' If animating, summarize station data, reset timestep, and refresh display
    ''' </summary>
    Private Sub ResetTrackbar()
        Try
            If SuspendMapUpdate Or Not ModeSettings.AllowAnimation Then Exit Sub
            SetAnimationTimeStep()
            If Mode = enumMode.OutputData Then
                SummarizeStationData()
            ElseIf Mode = enumMode.Animate Then
                With AnimationInfo
                    If Not .IsAnimating Then WriteProgress("Compiling data...")
                    If .dsData IsNot Nothing Then .CurrentResults = .dsData.GetResults(.StationList, cboPCode.Text, .ThisDate, .NextDate, DisplayMode)
                    If Not .IsAnimating Then ProgressComplete()
                End With
            End If
            RefreshStationsDisplay()
            ToolTip.Active = Not AnimationInfo.IsAnimating
        Catch ex As Exception
            AnimTimer.Stop()
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Show text on statusbar (causes progressbar to be marquee)
    ''' </summary>
    ''' <param name="FormatString">Text to display on statuslabel (may include formatting text for String.Format)</param>
    ''' <param name="Args">Optional arguments to pass to FormatString</param>
    Public Sub WriteProgress(ByVal FormatString As String, ByVal ParamArray Args() As Object)
        Try
            ProgressBar.Visible = True
            ProgressBar.Style = ProgressBarStyle.Marquee
            StatusLabel.Text = String.Format(FormatString, Args)
            StatusStripGIS.Refresh()
            Application.UseWaitCursor = True
            Application.DoEvents()
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Show text and task progress on statusbar 
    ''' </summary>
    ''' <param name="Value">Current value for progressbar</param>
    ''' <param name="MaxValue">Maximum value for progressbar</param>
    ''' <param name="FormatString">Text to display on statuslabel (may include formatting text for String.Format)</param>
    ''' <param name="Args">Optional arguments to pass to FormatString</param>
    Public Sub WriteProgress(ByVal Value As Integer, ByVal MaxValue As Integer, ByVal FormatString As String, ByVal ParamArray Args() As Object)
        Try
            Static LastTimer As Integer = My.Computer.Clock.TickCount
            If My.Computer.Clock.TickCount - LastTimer < 500 Then Exit Sub 'don't let it update too frequently!
            With ProgressBar
                .Style = ProgressBarStyle.Blocks
                .Maximum = MaxValue
                .Value = Value
                .Visible = True
            End With
            StatusLabel.Text = String.Format(FormatString, Args)
            StatusStripGIS.Refresh()
            Application.UseWaitCursor = True
            Application.DoEvents()
            LastTimer = My.Computer.Clock.TickCount
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub ZoomExtents()
        GISMap.ViewExtents = GetLayer(GISMap, lstLayers.Text).Extent
    End Sub

    Private Sub mnuFileExport_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFileExport.Click, tbExport.Click
        Try
            frmInfoMsg.ShowInfoMsg("Export Data", "This will allow you to export data for the currently thematic map for the currently selected PCode, type of data, aggregation period, and time step to a separate export file which can be used in an external GIS application. The export file can be in either of two formats:\n\n\t\b Shape file: contains all polygon or point shapes and the attribute table will have the current data file values appended. The GIS application can use the data fields to prepare a thematic map (the current thematic mapping scheme cannot be preserved).\n\n\t\b dBase table: contains only the shape keys and data values. The GIS application can 'join' this table to the original shape file so that the data field can be used to display a thematic map.")
            Dim db As New clsDBDBase
            Dim ThisDateLabel As String = Choose(DisplayMode() + 1, "ALL", "NUM", "MIN", "MAX", "AVG") & "_" & cboPCode.Text.Split(" ")(0)
            If Aggregate = enumAggregate.POR Then
                ThisDateLabel &= "_POR"
            Else
                ThisDateLabel &= String.Format("_{0:yyyyMMdd}", AnimationInfo.ThisDate)
            End If
            Dim ExportFile As String = ""
            With New SaveFileDialog
                .AddExtension = True
                .CheckPathExists = True
                .CreatePrompt = False
                '.DefaultExt = ".DBF"
                .Filter = "Shape File (*.shp)|*.shp|dBase Table (*.dbf)|*.dbf"
                .FilterIndex = 0
                .OverwritePrompt = True
                .Title = "Export Data File"
                .ValidateNames = True
                .InitialDirectory = GetSetting(REGAPP, Me.Name, "AddLayer", My.Computer.FileSystem.SpecialDirectories.MyDocuments & "WRDB")
                .FileName = String.Format("WRDBGIS_{0}", ThisDateLabel)
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then ExportFile = .FileName
                .Dispose()
            End With

            If ExportFile = "" Then Exit Sub

            If IO.Path.GetExtension(ExportFile).ToUpper = ".SHP" Then
                'create new shape file with all shapes in source file plus data field
                Dim ShapeFileName As String = ""
                Select Case Mode
                    Case enumMode.OutputData
                        ShapeFileName = WRDBShapeFileName
                    Case enumMode.Animate
                        ShapeFileName = AnimationInfo.DataLayer.DataSet.Filename
                End Select

                Select Case Mode
                    Case enumMode.Animate
                        AnimationInfo.DataLayer.DataSet.SaveAs(ExportFile, True)
                    Case enumMode.OutputData
                        WRDBLayer.DataSet.SaveAs(ExportFile, True)
                End Select

                'Dim sf As New DotSpatial.Data.PolygonShapefile
                'sf.Open(ExportFile)

                ''see if data field already in table
                'Dim FieldName As String = String.Format("{0}", ThisDateLabel)
                'If FieldName.Length > 10 Then FieldName = FieldName.Substring(0, 10) '.dbf field name max len =10

                'If sf.DataTable.Columns.IndexOf(FieldName) = -1 Then 'not found, add it
                '    sf.DataTable.Columns.Add(FieldName, GetType(Double))
                'End If

                'Dim staidx As Integer = GetFieldIndex(sf.DataTable, AnimationInfo.DataField)

                ''update value for each shape
                'For i As Integer = 0 To sf.Features.Count - 1
                '    Dim StaID As String = sf.DataTable.Rows(i)(staidx)
                '    Dim Value As Double = AnimationInfo.CurrentResults(StaID)
                '    If Value >= 0 Then sf.DataTable.Rows(i)(FieldName) = Value
                '    WriteProgress(i, sf.Features.Count - 1, "Saving data...")
                'Next

                ''close file
                'sf.Save()
                'sf.Close()

            Else
                'write stand-alone .dbf file that can be joined with original shape file
                'field names 10 char max, start with character, 128 fields max
                'get field names for each time step

                With db

                    .DatabaseType = clsDB.enumDBType.DBase
                    .DataSource = IO.Path.GetDirectoryName(ExportFile)
                    Dim TableName As String = IO.Path.GetFileNameWithoutExtension(ExportFile)
                    .CreateConnection()

                    Dim FieldNames As New Generic.List(Of String)
                    Dim FieldTypes As New Generic.List(Of clsDB.enumColType)

                    FieldNames.Add("Station_ID")
                    FieldTypes.Add(clsDB.enumColType.String)

                    FieldNames.Add(.GetValidObjectName(ThisDateLabel))
                    FieldTypes.Add(clsDB.enumColType.Double)

                    'if shapefile already exists, will be corrupted if dbf file has same name; delete shape file

                    DeleteShapeFiles(ExportFile)

                    If .TableExists(TableName) Then .DropTable(TableName)
                    If .CreateTable(TableName, FieldNames.ToArray, FieldTypes.ToArray) Then
                        Dim ds As New DataSet
                        Dim da As DbDataAdapter = .AddTable(ds, TableName)
                        If da IsNot Nothing Then
                            Dim dt As DataTable = ds.Tables(0)
                            Select Case Mode
                                Case enumMode.OutputData
                                    If dtResults IsNot Nothing Then
                                        dtResults.DefaultView.RowFilter = String.Format("MinDate>=#{0}# AND MinDate<#{1}#", AnimationInfo.ThisDate, AnimationInfo.NextDate)
                                        dtResults.DefaultView.Sort = "Station_ID"
                                        Dim FieldName As String = .GetValidObjectName(ThisDateLabel)
                                        For r As Integer = 0 To WRDBShapefile.Features.Count - 1
                                            Dim row As Integer = dtResults.DefaultView.Find(WRDBShapefile.DataTable.Rows(r)(0))
                                            Dim StaID As String = ""
                                            Dim value As Single = 0
                                            If row <> -1 Then
                                                StaID = TestNull(dtResults.DefaultView.Item(row).Item("Station_ID"), "")
                                                value = TestNull(dtResults.DefaultView.Item(row).Item(DisplayMode.ToString), 0)
                                            End If
                                            If StaID <> "" And value <> 0 Then
                                                Dim dr As DataRow = dt.NewRow
                                                dr.Item("Station_ID") = StaID
                                                dr.Item(FieldName) = value
                                                dt.Rows.Add(dr)
                                            End If
                                            WriteProgress(r, WRDBShapefile.Features.Count - 1, "Saving data...")
                                        Next
                                    End If
                                Case enumMode.SelectStations, enumMode.DisplayStations
                                Case enumMode.Animate
                                    Dim fld As Integer = GetFieldIndex(AnimationInfo.DataLayer.DataSet.DataTable, AnimationInfo.DataField)
                                    Dim FieldName As String = String.Format("{0}", .GetValidObjectName(ThisDateLabel))
                                    For r As Integer = 0 To AnimationInfo.DataLayer.DataSet.Features.Count - 1
                                        Dim StaID As String = AnimationInfo.DataLayer.DataSet.DataTable.Rows(r)(fld)
                                        Dim dr As DataRow = dt.NewRow
                                        dr.Item("Station_ID") = StaID
                                        Dim Value As Double = AnimationInfo.CurrentResults(StaID)
                                        If Value >= 0 Then
                                            dr.Item(FieldName) = Value
                                            dt.Rows.Add(dr)
                                        End If
                                        WriteProgress(r, AnimationInfo.DataLayer.DataSet.Features.Count - 1, "Saving data...")
                                    Next
                            End Select
                            WriteProgress("Saving data...")
                            Dim NumUpdated As Integer = da.Update(ds, TableName)
                            ds.Dispose()
                            da.Dispose()
                        End If
                    End If
                    .Dispose()
                End With
            End If

        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

    Private Sub DrawLegend()
        Try
            'create legend squares samewidth as picturebox, and height = 12 points

            With Me.picLegend

                .Font = New Font("Arial", 8)
                Dim g As Graphics = .CreateGraphics
                Dim BoxHeight As Int16 = g.MeasureString("0.00", .Font).Height * 1.5
                Dim NumEntries As Int16 = Int(.Height / BoxHeight)
                If NumEntries <= 1 Then Exit Sub
                Dim polyScheme As New DotSpatial.Symbology.PolygonScheme
                For i As Int16 = 0 To NumEntries - 1
                    Dim fillcolor As Color = GradientColor(i * 1.0 / (NumEntries - 1))
                    Dim br As New SolidBrush(fillcolor)
                    g.FillRectangle(br, New Rectangle(0, BoxHeight * i, .Width, BoxHeight))
                    Dim fmt As New Drawing.StringFormat
                    fmt.Alignment = StringAlignment.Center
                    fmt.LineAlignment = StringAlignment.Center
                    Dim pnt As PointF
                    'draw white on black
                    pnt = New PointF(.Width / 2 + 1, (i + 0.5) * BoxHeight + 1)
                    Dim value, catRange As Double
                    With AnimationInfo
                        catRange = (.MaxValue - .MinValue) / (NumEntries) + 0.01
                        value = .MinValue + (i + 0.5) * catRange
                    End With
                    g.DrawString(value.ToString("N3"), .Font, Brushes.Black, pnt, fmt)
                    pnt = New PointF(.Width / 2, (i + 0.5) * BoxHeight)
                    g.DrawString(value.ToString("N3"), .Font, Brushes.White, pnt, fmt)

                    'create new category for symbology
                    Dim pc As New DotSpatial.Symbology.PolygonCategory(fillcolor, Color.Gray, 1)
                    pc.FilterExpression = String.Format("Result>={0} and Result<{1}", value - catRange / 2, value + catRange / 2)

                    'add category to scheme
                    polyScheme.AddCategory(pc)
                Next

                'create new category for symbology
                Dim pcNull As New DotSpatial.Symbology.PolygonCategory(AnimationInfo.Colors(0), Color.Gray, 1)
                pcNull.FilterExpression = String.Format("Result IS NULL")

                'add category to scheme
                polyScheme.AddCategory(pcNull)

                'set scheme to layer
                AnimationInfo.DataLayer.Symbology = polyScheme
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuViewLegend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewLegend.Click, tbLegend.Click
        mnuViewLegend.Checked = Not mnuViewLegend.Checked
        picLegend.Visible = mnuViewLegend.Checked
    End Sub

    Private Sub mnuDataOptions_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuDataOptions.Click
        Try
            With New frmGISOptions
                .cboColor0.Text = AnimationInfo.Colors(0).Name
                .cboColor1.Text = AnimationInfo.Colors(1).Name
                .cboColor2.Text = AnimationInfo.Colors(2).Name
                .cboColor3.Text = AnimationInfo.Colors(3).Name
                .cboColor4.Text = AnimationInfo.Colors(4).Name
                .chkInvert.Checked = AnimationInfo.InvertColor
                .cboThreshold.SelectedIndex = AnimationInfo.ThresholdType
                .txtThreshold.Text = AnimationInfo.ThresholdValue
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    AnimationInfo.Colors(0) = Color.FromName(.cboColor0.Text)
                    AnimationInfo.Colors(1) = Color.FromName(.cboColor1.Text)
                    AnimationInfo.Colors(2) = Color.FromName(.cboColor2.Text)
                    AnimationInfo.Colors(3) = Color.FromName(.cboColor3.Text)
                    AnimationInfo.Colors(4) = Color.FromName(.cboColor4.Text)
                    AnimationInfo.InvertColor = .chkInvert.Checked
                    AnimationInfo.ThresholdType = .cboThreshold.SelectedIndex
                    AnimationInfo.ThresholdValue = .txtThreshold.Text
                    RefreshStationsDisplay()
                    DrawLegend()
                    IsModified = True
                End If
                .Dispose()
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            ProgressComplete()
        End Try
    End Sub

    Private Sub mnuFileMovie_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFileMovie.Click
        If Aggregate = enumAggregate.POR Then
            WarningMsg("You cannot create a movie unless the aggregation period is something other than Period of Record.")
            Exit Sub
        End If
        Dim aviMgr As AviFile.AviManager = Nothing
        Try
            Dim ThisLabel As String = Choose(DisplayMode() + 1, "ALL", "NUM", "MIN", "MAX", "AVG") & "_" & cboPCode.Text
            Dim MovieFile As String = ""
            With New SaveFileDialog
                .FileName = IO.Path.ChangeExtension(GISFileName, ".avi")
                .AddExtension = True
                .CheckPathExists = True
                .CreatePrompt = False
                .DefaultExt = ".avi"
                .Filter = "Movie File (*.avi)|*.avi"
                .FilterIndex = 0
                .OverwritePrompt = True
                .Title = "Create Animation Movie"
                .ValidateNames = True
                .FileName = String.Format("WRDBGIS_{0}.avi", ThisLabel)
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then MovieFile = .FileName
                .Dispose()
            End With

            If MovieFile = "" Then Exit Sub

            Try
                If My.Computer.FileSystem.FileExists(MovieFile) Then My.Computer.FileSystem.DeleteFile(MovieFile)
            Catch ex As Exception
                ErrorMsg("Unable to delete old video file; it might be open by another application.", ex)
                Exit Sub
            End Try

            With trkAnim

                .Value = .Minimum

                Try
                    aviMgr = New AviFile.AviManager(MovieFile, False)
                Catch ex As Exception
                    ErrorMsg("Unable to create video file; it might be open by another application.", ex)
                    Exit Sub
                End Try

                Dim aviStr As AviFile.VideoStream = Nothing

                Do
                    Application.DoEvents()
                    If .Value = .Minimum Then
                        Try
                            Dim framerate As Double = Choose(AnimationInfo.Speed, 2.4, 3, 4.8, 8, 12) 'determined empirically
                            aviStr = aviMgr.AddVideoStream(True, framerate, MapSnapshot)
                        Catch ex As Exception
                            ErrorMsg("Unable to create video file; it might be open by another application or the selected compression scheme may be unsupported on your computer.", ex)
                            Exit Sub
                        End Try
                    Else
                        aviStr.AddFrame(MapSnapshot)
                    End If
                    If IsCancelled Or .Value + .TickFrequency > .Maximum Then Exit Do
                    .Value += .TickFrequency
                Loop

                .Value = .Minimum
            End With

            Process.Start(MovieFile)
        Catch ex As Exception
            ErrorMsg(, ex)
        Finally
            If aviMgr IsNot Nothing Then aviMgr.Close()
        End Try
    End Sub

    Private Function MapSnapshot() As Bitmap
        Try
            'take snapshot of entire form

            Dim g1 As System.Drawing.Graphics = Me.CreateGraphics()
            Dim MyImage As New System.Drawing.Bitmap(Width, Height, g1)

            Dim g2 As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(MyImage)
            Dim dc1 As IntPtr = g1.GetHdc()
            Dim dc2 As IntPtr = g2.GetHdc()
            BitBlt(dc2, 0, 0, Me.ClientSize.Width, Me.ClientSize.Height, dc1, 0, 0, 13369376)
            g1.ReleaseHdc(dc1)
            g2.ReleaseHdc(dc2)

            'crop only the map and legend
            Return Crop(MyImage)
        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    Private Function Crop(ByVal bmForm As Bitmap) As Bitmap
        Try

            'copy part of form that has map and legend
            Dim r, w, h As Integer
            With picLegend
                r = .PointToScreen(New Point(.Width, .Height)).X
            End With
            With GISMap
                w = r - .PointToScreen(New Point(0, 0)).X
                h = .Height
            End With

            Dim bmMap As New Bitmap(w, h)

            Dim g2 As Graphics = Graphics.FromImage(bmMap)

            Dim pt As Point = GISMap.PointToScreen(New Point(0, 0))
            g2.DrawImage(bmForm, New Rectangle(0, 0, w, h), New Rectangle(Me.PointToClient(pt), New Size(w, h)), GraphicsUnit.Pixel)

            Dim fmt As New Drawing.StringFormat
            fmt.Alignment = StringAlignment.Near
            fmt.LineAlignment = StringAlignment.Near
            Dim s As String = String.Format("{0} of {1} aggregated by {2}: {3:" & MyDateFormat() & "}{4}", cboDisplay.Text, cboPCode.Text.Split(" ")(0), cboAggregate.Text, AnimationInfo.ThisDate, IIf(AnimationInfo.ThresholdType = enumThreshold.None, "", "; Color threshold=" & AnimationInfo.ThresholdValue))
            Dim ms As SizeF = g2.MeasureString(s, picLegend.Font, 0)
            g2.FillRectangle(Brushes.Black, 0, 0, ms.Width, ms.Height)
            g2.DrawString(s, picLegend.Font, Brushes.White, New Point(0, 0), fmt)

            Return bmMap

        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    Private Function MapSnapshotold3() As Bitmap
        Try
            Dim g1 As System.Drawing.Graphics = GISMap.CreateGraphics()
            Dim MyImage As New System.Drawing.Bitmap(GISMap.ClientRectangle.Width, GISMap.ClientRectangle.Height, g1)

            'copy part of form that has map and legend
            Dim r, w, h As Integer
            With picLegend
                r = .PointToScreen(New Point(.Right, .Bottom)).X
            End With
            With GISMap
                w = r - .PointToScreen(New Point(0, 0)).X
                w = GISMap.Width + picLegend.Width
                h = .Height
            End With

            Dim bmMap As New Bitmap(w, h)

            Dim g2 As Graphics = Graphics.FromImage(bmMap)

            Dim dc1 As IntPtr = g1.GetHdc()
            Dim dc2 As IntPtr = g2.GetHdc()

            BitBlt(dc2, 0, 0, w, h, dc1, 0, 0, 13369376)
            g1.ReleaseHdc(dc1)
            g2.ReleaseHdc(dc2)
            Return MyImage
        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    Private Sub mnuViewLabels_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuViewLabels.Click, tbLabels.Click
        ActiveLayer = clsLayer.Reset(GISMap, WRDBLayerName)
        ActiveLayer.LabelVisible = mnuViewLabels.Checked
        RefreshStationsDisplay()
    End Sub

    Delegate Sub ProgressDelegate(Value As Integer, MaxValue As Integer, ProgressText As String)

    Private Sub Progress(Value As Integer, MaxValue As Integer, ProgressText As String)
        WriteProgress(Value, MaxValue, ProgressText)
    End Sub

    Private Sub picLegend_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles picLegend.Paint
        DrawLegend()
    End Sub

    Private Sub mnuToolsProjectionChange_Click(sender As System.Object, e As System.EventArgs) Handles mnuToolsProjectionChange.Click
        With New DotSpatial.Controls.MapFrameProjectionDialog(GISMap.MapFrame)
            If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                ProjectProjection = GISMap.MapFrame.Projection
            End If
        End With
    End Sub

    Private Sub mnuToolsProjectionAssign_Click(sender As System.Object, e As System.EventArgs) Handles mnuToolsProjectionAssign.Click
        Try
            With New OpenFileDialog()
                .Filter = "Shape Files (*.shp)|*.shp"
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    Dim fs As New DotSpatial.Data.FeatureSet
                    fs.Open(.FileName)
                    If MessageBox.Show(String.Format("This file's projection is: {0}; do you want to reset it to a different projection? (Note that the coordinates of the shapes will not change, just the projection associated with the file).", fs.Projection.ToString), "Set Projection", MessageBoxButtons.OKCancel) = Windows.Forms.DialogResult.OK Then
                        With New DotSpatial.Projections.Forms.ProjectionSelectDialog()
                            If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                                fs.Projection = .SelectedCoordinateSystem
                                fs.Save()
                                If WarningMsg("Do you want to add this reprojected file to your map?") = Windows.Forms.DialogResult.OK Then
                                    GISMap.Layers.Add(fs.Filename)
                                    RefreshLayerList()
                                End If
                            End If
                        End With
                    End If
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuToolsMeasure_Click(sender As System.Object, e As System.EventArgs) Handles mnuToolsMeasure.Click
        With New DotSpatial.Controls.MeasureDialog
            GISMap.FunctionMode = DotSpatial.Controls.FunctionMode.Measure
        End With
    End Sub

    Private Sub mnuToolsProjectionReproject_Click(sender As System.Object, e As System.EventArgs) Handles mnuToolsProjectionReproject.Click
        Try
            With New OpenFileDialog()
                .Filter = "Shape Files (*.shp)|*.shp"
                If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                    Dim fs As New DotSpatial.Data.FeatureSet
                    fs.Open(.FileName)
                    If Not fs.Projection.IsValid Then
                        WarningMsg("This shape file has no projection information assigned to it; it is assumed to use native latitude/longitude coordinates (WGS 1984).")
                        fs.Projection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984
                    End If
                    If MessageBox.Show(String.Format("This file's projection is: {0}; do you want to reproject the file to a different projection? This will change the coordinates so that they match the new projection that will be assigned, and you can save the new file under a different name.", fs.Projection.ToString), "Reproject Shape File", MessageBoxButtons.OKCancel) = Windows.Forms.DialogResult.OK Then
                        With New DotSpatial.Projections.Forms.ProjectionSelectDialog()
                            If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                                fs.Reproject(.SelectedCoordinateSystem)
                                With New SaveFileDialog
                                    .FileName = IO.Path.GetFileNameWithoutExtension(fs.Filename) & "-Reprojected"
                                    .Filter = "Shape Files (*.shp)|*.shp"
                                    .OverwritePrompt = True
                                    If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                                        If String.Equals(fs.Filename, .FileName, StringComparison.CurrentCultureIgnoreCase) Then
                                            WarningMsg("You cannot overwrite the original file when you reproject.")
                                            Exit Sub
                                        End If
                                        fs.SaveAs(.FileName, True)
                                        If WarningMsg("Do you want to add this reprojected file to your map?") = Windows.Forms.DialogResult.OK Then
                                            GISMap.Layers.Add(.FileName)
                                            RefreshLayerList()
                                        End If
                                    End If
                                End With
                            End If
                        End With

                    End If
                End If
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    Private Sub mnuToolsOptions_Click(sender As Object, e As System.EventArgs) Handles mnuToolsOptions.Click
        With New frmOptions
            .txtAnimation.Text = GetSetting(REGAPP, "AutoLoad", "Animation")
            If .ShowDialog(Me) = Windows.Forms.DialogResult.OK Then
                SaveSetting(REGAPP, "AutoLoad", "Animation", .txtAnimation.Text)
            End If
        End With
    End Sub

    Private Sub mnuLayerShowAll_Click(sender As System.Object, e As System.EventArgs) Handles mnuLayerShowAll.Click, mnuLayerShowAll2.Click, mnuLayerHideAll.Click, mnuLayerHideAll2.Click
        For i As Integer = 0 To lstLayers.Items.Count - 1
            lstLayers.SetItemChecked(i, sender Is mnuLayerShowAll Or sender Is mnuLayerShowAll2)
        Next
    End Sub
End Class

''' <summary>
''' This class exposes the MouseWheel event on the AxMap control
''' </summary>
Public Class MouseWheelMap
    Inherits DotSpatial.Controls.Map

    Public Event MouseWheelDownEvent(ByVal sender As Object, ByVal e As MouseEventArgs)

    ' This is the event that should be intercepted to receive the mouse wheel event
    'Public Event MouseWheelScrollEvent(ByVal sender As Object, ByVal e As MouseEventArgs)
    Public Event MouseWheelUpEvent(ByVal sender As Object, ByVal e As MouseEventArgs)
    Public Const MK_LBUTTON As Integer = &H1
    Public Const MK_MBUTTON As Integer = &H10
    Public Const MK_RBUTTON As Integer = &H2
    Public Const WM_MBUTTONDOWN As Integer = &H207
    Public Const WM_MBUTTONUP As Integer = &H208
    Public Const WM_MOUSEMOVE As Integer = &H200

    Public Const WM_MOUSEWHEEL As Integer = &H20A

    ' This method traps all windows messages, but only responds to the MouseWheel message
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        'If m.Msg = WM_MOUSEWHEEL Or m.Msg = WM_MBUTTONUP Or m.Msg = WM_MBUTTONDOWN Then
        If m.Msg = WM_MOUSEMOVE Then
            Dim xpos, ypos, mouseKeys, rotation, delta As Integer
            xpos = m.LParam.ToInt32() And 65535
            ypos = m.LParam.ToInt32() / 65536
            mouseKeys = m.WParam.ToInt32() And 65535
            rotation = m.WParam.ToInt32() / 65536
            delta = rotation / 120
            Dim mb As MouseButtons
            Select Case mouseKeys
                Case MK_LBUTTON : mb = Windows.Forms.MouseButtons.Left
                Case MK_RBUTTON : mb = Windows.Forms.MouseButtons.Right
                Case MK_MBUTTON : mb = Windows.Forms.MouseButtons.Middle
                Case Else : mb = Windows.Forms.MouseButtons.None
            End Select
            If mb = MouseButtons.Middle Then MyBase.OnMouseMove(New MouseEventArgs(Windows.Forms.MouseButtons.Left, 1, xpos, ypos, delta)) : Exit Sub

        ElseIf (m.Msg = WM_MBUTTONUP Or m.Msg = WM_MBUTTONDOWN) Then

            Dim xpos, ypos, mouseKeys, rotation, delta As Integer
            xpos = m.LParam.ToInt32() And 65535
            ypos = m.LParam.ToInt32() / 65536
            mouseKeys = m.WParam.ToInt32() And 65535
            rotation = m.WParam.ToInt32() / 65536
            delta = rotation / 120
            Dim mb As MouseButtons
            Select Case mouseKeys
                Case MK_LBUTTON : mb = Windows.Forms.MouseButtons.Left
                Case MK_RBUTTON : mb = Windows.Forms.MouseButtons.Right
                Case MK_MBUTTON : mb = Windows.Forms.MouseButtons.Middle
                Case Else : mb = Windows.Forms.MouseButtons.None
            End Select

            Select Case m.Msg
                'Case WM_MOUSEWHEEL
                '    RaiseEvent MouseWheelScrollEvent(Me, New MouseEventArgs(mb, 0, xpos, ypos, delta))
                Case WM_MBUTTONDOWN
                    MyBase.FunctionMode = DotSpatial.Controls.FunctionMode.Pan
                    'RaiseEvent MouseWheelDownEvent(Me, New MouseEventArgs(mb, 0, xpos, ypos, delta))
                Case WM_MBUTTONUP
                    MyBase.FunctionMode = DotSpatial.Controls.FunctionMode.Select
                    'RaiseEvent MouseWheelUpEvent(Me, New MouseEventArgs(mb, 0, xpos, ypos, delta))
            End Select
            Exit Sub
        End If

        MyBase.WndProc(m)
    End Sub

End Class
